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 001/190] #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 002/190] #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 003/190] #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 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 004/190] #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 005/190] #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 006/190] #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 007/190] 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 008/190] 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 009/190] #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 010/190] #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 011/190] #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 012/190] #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 013/190] 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 014/190] #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 015/190] #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 016/190] #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 017/190] #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 018/190] #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 019/190] #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 020/190] 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 021/190] 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 022/190] 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 023/190] 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 024/190] 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 025/190] #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 026/190] 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 027/190] 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 028/190] #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 029/190] #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 030/190] #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 031/190] #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 032/190] 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!
-[](https://search.maven.org/search?q=g:org.mapstruct%20AND%20v:1.*.Final)
-[](https://search.maven.org/search?q=g:org.mapstruct)
+[](https://central.sonatype.com/search?q=g:org.mapstruct%20v:1.6.0)
+[](https://central.sonatype.com/search?q=g:org.mapstruct)
[](https://github.com/mapstruct/mapstruct/blob/main/LICENSE.txt)
[](https://github.com/mapstruct/mapstruct/actions?query=branch%3Amain+workflow%3ACI)
[](https://codecov.io/gh/mapstruct/mapstruct/tree/main)
-[](https://gitter.im/mapstruct/mapstruct-users)
-[](https://lgtm.com/projects/g/mapstruct/mapstruct/context:java)
-[](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 033/190] 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 034/190] 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 035/190] 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 036/190] #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>()#if>
+ <#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/>()#if>
+ new <@includeModel object=resultType.implementationType/><#if ext.useSizeIfPossible?? && ext.useSizeIfPossible && canUseSize>( <@sizeForCreation /> )<#else>()#if>
#if>
+ <#else>
+ new <@includeModel object=resultType/>()
+ #if>
@compress>
<#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 extends Auction> 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 super AuctionDto> mapSuper(List auctions) {
+ if ( auctions == null ) {
+ return null;
+ }
+
+ List super AuctionDto> list = new ArrayList( auctions.size() );
+ for ( Auction auction : auctions ) {
+ list.add( map( auction ) );
+ }
+
+ return list;
+ }
+
+ @Override
+ public Map mapExtend(Map extends Auction, ? extends Auction> auctions) {
+ if ( auctions == null ) {
+ return null;
+ }
+
+ Map map = LinkedHashMap.newLinkedHashMap( auctions.size() );
+
+ for ( java.util.Map.Entry extends Auction, ? extends Auction> entry : auctions.entrySet() ) {
+ AuctionDto key = map( entry.getKey() );
+ AuctionDto value = map( entry.getValue() );
+ map.put( key, value );
+ }
+
+ return map;
+ }
+
+ @Override
+ public Map super AuctionDto, ? super AuctionDto> mapSuper(Map auctions) {
+ if ( auctions == null ) {
+ return null;
+ }
+
+ Map super AuctionDto, ? super AuctionDto> 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 037/190] 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 038/190] 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 039/190] 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 040/190] #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