diff --git a/core/src/main/java/org/mapstruct/Condition.java b/core/src/main/java/org/mapstruct/Condition.java
index 148ec4879..37f1553be 100644
--- a/core/src/main/java/org/mapstruct/Condition.java
+++ b/core/src/main/java/org/mapstruct/Condition.java
@@ -24,6 +24,7 @@ import java.lang.annotation.Target;
*
The mapping source parameter
* {@code @}{@link Context} parameter
* {@code @}{@link TargetPropertyName} parameter
+ * {@code @}{@link SourcePropertyName} parameter
*
*
* Note: The usage of this annotation is mandatory
diff --git a/core/src/main/java/org/mapstruct/SourcePropertyName.java b/core/src/main/java/org/mapstruct/SourcePropertyName.java
new file mode 100644
index 000000000..a9d036d5d
--- /dev/null
+++ b/core/src/main/java/org/mapstruct/SourcePropertyName.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation marks a presence check method parameter as a source property name parameter.
+ *
+ * This parameter enables conditional filtering based on source property name at run-time.
+ * Parameter must be of type {@link String} and can be present only in {@link Condition} method.
+ *
+ *
+ * @author Oliver Erhart
+ * @since 1.6
+ */
+@Target(ElementType.PARAMETER)
+@Retention(RetentionPolicy.CLASS)
+public @interface SourcePropertyName {
+}
diff --git a/core/src/main/java/org/mapstruct/TargetPropertyName.java b/core/src/main/java/org/mapstruct/TargetPropertyName.java
index 17af966fa..c7ab8d957 100644
--- a/core/src/main/java/org/mapstruct/TargetPropertyName.java
+++ b/core/src/main/java/org/mapstruct/TargetPropertyName.java
@@ -11,10 +11,10 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
- * This annotation marks a presence check method parameter as a property name parameter.
+ * This annotation marks a presence check method parameter as a target property name parameter.
*
- * This parameter enables conditional filtering based on target property name at run-time.
- * Parameter must be of type {@link String} and can be present only in {@link Condition} method.
+ * This parameter enables conditional filtering based on target property name at run-time.
+ * Parameter must be of type {@link String} and can be present only in {@link Condition} method.
*
* @author Nikola Ivačič
* @since 1.6
diff --git a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc
index 9d6c93f21..db13c0548 100644
--- a/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc
+++ b/documentation/src/main/asciidoc/chapter-10-advanced-mapping-options.asciidoc
@@ -406,9 +406,9 @@ public class CarMapperImpl implements CarMapper {
----
====
-Additionally `@TargetPropertyName` of type `java.lang.String` can be used in custom condition check method:
+Additionally `@TargetPropertyName` or `@SourcePropertyName` of type `java.lang.String` can be used in custom condition check method:
-.Mapper using custom condition check method with `@TargetPropertyName`
+.Mapper using custom condition check method with `@TargetPropertyName` and `@SourcePropertyName`
====
[source, java, linenums]
[subs="verbatim,attributes"]
@@ -416,11 +416,19 @@ Additionally `@TargetPropertyName` of type `java.lang.String` can be used in cus
@Mapper
public interface CarMapper {
+ @Mapping(target = "owner", source = "ownerName")
CarDto carToCarDto(Car car, @MappingTarget CarDto carDto);
@Condition
- default boolean isNotEmpty(String value, @TargetPropertyName String name) {
- if ( name.equals( "owner" ) {
+ default boolean isNotEmpty(
+ String value,
+ @TargetPropertyName String targetPropertyName,
+ @SourcePropertyName String sourcePropertyName
+ ) {
+
+ if ( targetPropertyName.equals( "owner" )
+ && sourcePropertyName.equals( "ownerName" ) ) {
+
return value != null
&& !value.isEmpty()
&& !value.equals( value.toLowerCase() );
@@ -431,7 +439,7 @@ public interface CarMapper {
----
====
-The generated mapper with `@TargetPropertyName` will look like:
+The generated mapper with `@TargetPropertyName` and `@SourcePropertyName` will look like:
.Custom condition check in generated implementation
====
@@ -447,7 +455,7 @@ public class CarMapperImpl implements CarMapper {
return carDto;
}
- if ( isNotEmpty( car.getOwner(), "owner" ) ) {
+ if ( isNotEmpty( car.getOwner(), "owner", "ownerName" ) ) {
carDto.setOwner( car.getOwner() );
} else {
carDto.setOwner( null );
@@ -470,7 +478,7 @@ If there is a custom `@Condition` method applicable for the property it will hav
====
Methods annotated with `@Condition` in addition to the value of the source property can also have the source parameter as an input.
-`@TargetPropertyName` parameter can only be used in `@Condition` methods.
+`@TargetPropertyName` and `@SourcePropertyName` parameters can only be used in `@Condition` methods.
====
<> is also valid for `@Condition` methods.
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java b/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java
index 9ac13184c..2ed9bd9a0 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java
@@ -31,6 +31,7 @@ import org.mapstruct.Mappings;
import org.mapstruct.Named;
import org.mapstruct.ObjectFactory;
import org.mapstruct.Qualifier;
+import org.mapstruct.SourcePropertyName;
import org.mapstruct.SubclassMapping;
import org.mapstruct.SubclassMappings;
import org.mapstruct.TargetPropertyName;
@@ -57,6 +58,7 @@ import org.mapstruct.tools.gem.GemDefinition;
@GemDefinition(BeanMapping.class)
@GemDefinition(EnumMapping.class)
@GemDefinition(MapMapping.class)
+@GemDefinition(SourcePropertyName.class)
@GemDefinition(SubclassMapping.class)
@GemDefinition(SubclassMappings.class)
@GemDefinition(TargetType.class)
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java
index ee347ef35..be7203507 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java
@@ -1522,6 +1522,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
MappingReferences mappingRefs = extractMappingReferences( targetPropertyName, false );
PropertyMapping propertyMapping = new PropertyMappingBuilder().mappingContext( ctx )
.sourceMethod( method )
+ .sourcePropertyName( targetPropertyName )
.target( targetPropertyName, targetPropertyReadAccessor, targetPropertyWriteAccessor )
.sourceReference( sourceRef )
.existingVariableNames( existingVariableNames )
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java
index 50f23ecbf..2f3095796 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java
@@ -70,6 +70,7 @@ import static org.mapstruct.ap.internal.model.common.Assignment.AssignmentType.D
public class PropertyMapping extends ModelElement {
private final String name;
+ private final String sourcePropertyName;
private final String sourceBeanName;
private final String targetWriteAccessorName;
private final ReadAccessor targetReadAccessorProvider;
@@ -286,6 +287,7 @@ public class PropertyMapping extends ModelElement {
}
return new PropertyMapping(
+ sourcePropertyName,
targetPropertyName,
rightHandSide.getSourceParameterName(),
targetWriteAccessor.getSimpleName(),
@@ -1099,16 +1101,17 @@ public class PropertyMapping extends ModelElement {
ReadAccessor targetReadAccessorProvider,
Type targetType, Assignment propertyAssignment,
Set dependsOn, Assignment defaultValueAssignment, boolean constructorMapping) {
- this( name, null, targetWriteAccessorName, targetReadAccessorProvider,
+ this( name, null, null, targetWriteAccessorName, targetReadAccessorProvider,
targetType, propertyAssignment, dependsOn, defaultValueAssignment,
constructorMapping
);
}
- private PropertyMapping(String name, String sourceBeanName, String targetWriteAccessorName,
- ReadAccessor targetReadAccessorProvider, Type targetType,
- Assignment assignment,
- Set dependsOn, Assignment defaultValueAssignment, boolean constructorMapping) {
+ private PropertyMapping(String sourcePropertyName, String name, String sourceBeanName,
+ String targetWriteAccessorName, ReadAccessor targetReadAccessorProvider, Type targetType,
+ Assignment assignment,
+ Set dependsOn, Assignment defaultValueAssignment, boolean constructorMapping) {
+ this.sourcePropertyName = sourcePropertyName;
this.name = name;
this.sourceBeanName = sourceBeanName;
this.targetWriteAccessorName = targetWriteAccessorName;
@@ -1128,6 +1131,10 @@ public class PropertyMapping extends ModelElement {
return name;
}
+ public String getSourcePropertyName() {
+ return sourcePropertyName;
+ }
+
public String getSourceBeanName() {
return sourceBeanName;
}
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Parameter.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Parameter.java
index 600c8ac33..44ac0eb7f 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Parameter.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Parameter.java
@@ -14,8 +14,9 @@ import javax.lang.model.element.VariableElement;
import org.mapstruct.ap.internal.gem.ContextGem;
import org.mapstruct.ap.internal.gem.MappingTargetGem;
-import org.mapstruct.ap.internal.gem.TargetTypeGem;
+import org.mapstruct.ap.internal.gem.SourcePropertyNameGem;
import org.mapstruct.ap.internal.gem.TargetPropertyNameGem;
+import org.mapstruct.ap.internal.gem.TargetTypeGem;
import org.mapstruct.ap.internal.util.Collections;
/**
@@ -32,6 +33,7 @@ public class Parameter extends ModelElement {
private final boolean mappingTarget;
private final boolean targetType;
private final boolean mappingContext;
+ private final boolean sourcePropertyName;
private final boolean targetPropertyName;
private final boolean varArgs;
@@ -44,12 +46,13 @@ public class Parameter extends ModelElement {
this.mappingTarget = MappingTargetGem.instanceOn( element ) != null;
this.targetType = TargetTypeGem.instanceOn( element ) != null;
this.mappingContext = ContextGem.instanceOn( element ) != null;
+ this.sourcePropertyName = SourcePropertyNameGem.instanceOn( element ) != null;
this.targetPropertyName = TargetPropertyNameGem.instanceOn( element ) != null;
this.varArgs = varArgs;
}
private Parameter(String name, Type type, boolean mappingTarget, boolean targetType, boolean mappingContext,
- boolean targetPropertyName,
+ boolean sourcePropertyName, boolean targetPropertyName,
boolean varArgs) {
this.element = null;
this.name = name;
@@ -58,12 +61,13 @@ public class Parameter extends ModelElement {
this.mappingTarget = mappingTarget;
this.targetType = targetType;
this.mappingContext = mappingContext;
+ this.sourcePropertyName = sourcePropertyName;
this.targetPropertyName = targetPropertyName;
this.varArgs = varArgs;
}
public Parameter(String name, Type type) {
- this( name, type, false, false, false, false, false );
+ this( name, type, false, false, false, false, false, false );
}
public Element getElement() {
@@ -99,6 +103,7 @@ public class Parameter extends ModelElement {
return ( mappingTarget ? "@MappingTarget " : "" )
+ ( targetType ? "@TargetType " : "" )
+ ( mappingContext ? "@Context " : "" )
+ + ( sourcePropertyName ? "@SourcePropertyName " : "" )
+ ( targetPropertyName ? "@TargetPropertyName " : "" )
+ "%s " + name;
}
@@ -120,6 +125,10 @@ public class Parameter extends ModelElement {
return targetPropertyName;
}
+ public boolean isSourcePropertyName() {
+ return sourcePropertyName;
+ }
+
public boolean isVarArgs() {
return varArgs;
}
@@ -165,6 +174,7 @@ public class Parameter extends ModelElement {
false,
false,
false,
+ false,
false
);
}
@@ -206,6 +216,10 @@ public class Parameter extends ModelElement {
return parameters.stream().filter( Parameter::isTargetType ).findAny().orElse( null );
}
+ public static Parameter getSourcePropertyNameParameter(List parameters) {
+ return parameters.stream().filter( Parameter::isSourcePropertyName ).findAny().orElse( null );
+ }
+
public static Parameter getTargetPropertyNameParameter(List parameters) {
return parameters.stream().filter( Parameter::isTargetPropertyName ).findAny().orElse( null );
}
@@ -214,6 +228,7 @@ public class Parameter extends ModelElement {
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 18274e549..0791ee626 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/ParameterBinding.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/ParameterBinding.java
@@ -22,16 +22,19 @@ public class ParameterBinding {
private final boolean targetType;
private final boolean mappingTarget;
private final boolean mappingContext;
+ private final boolean sourcePropertyName;
private final boolean targetPropertyName;
private final SourceRHS sourceRHS;
private ParameterBinding(Type parameterType, String variableName, boolean mappingTarget, boolean targetType,
- boolean mappingContext, boolean targetPropertyName, SourceRHS sourceRHS) {
+ boolean mappingContext, boolean sourcePropertyName, boolean targetPropertyName,
+ SourceRHS sourceRHS) {
this.type = parameterType;
this.variableName = variableName;
this.targetType = targetType;
this.mappingTarget = mappingTarget;
this.mappingContext = mappingContext;
+ this.sourcePropertyName = sourcePropertyName;
this.targetPropertyName = targetPropertyName;
this.sourceRHS = sourceRHS;
}
@@ -64,11 +67,18 @@ public class ParameterBinding {
return mappingContext;
}
+ /**
+ * @return {@code true}, if the parameter being bound is a {@code @SourcePropertyName} parameter.
+ */
+ public boolean isSourcePropertyName() {
+ return sourcePropertyName;
+ }
+
/**
* @return {@code true}, if the parameter being bound is a {@code @TargetPropertyName} parameter.
*/
public boolean isTargetPropertyName() {
- return targetPropertyName;
+ return targetPropertyName;
}
/**
@@ -108,6 +118,7 @@ public class ParameterBinding {
parameter.isMappingTarget(),
parameter.isTargetType(),
parameter.isMappingContext(),
+ parameter.isSourcePropertyName(),
parameter.isTargetPropertyName(),
null
);
@@ -129,6 +140,7 @@ public class ParameterBinding {
false,
false,
false,
+ false,
null
);
}
@@ -138,14 +150,21 @@ public class ParameterBinding {
* @return a parameter binding representing a target type parameter
*/
public static ParameterBinding forTargetTypeBinding(Type classTypeOf) {
- return new ParameterBinding( classTypeOf, null, false, true, false, false, null );
+ return new ParameterBinding( classTypeOf, null, false, true, false, false, false, null );
}
/**
* @return a parameter binding representing a target property name parameter
*/
public static ParameterBinding forTargetPropertyNameBinding(Type classTypeOf) {
- return new ParameterBinding( classTypeOf, null, false, false, false, true, null );
+ return new ParameterBinding( classTypeOf, null, false, false, false, false, true, null );
+ }
+
+ /**
+ * @return a parameter binding representing a source property name parameter
+ */
+ public static ParameterBinding forSourcePropertyNameBinding(Type classTypeOf) {
+ return new ParameterBinding( classTypeOf, null, false, false, false, true, false, null );
}
/**
@@ -153,7 +172,7 @@ public class ParameterBinding {
* @return a parameter binding representing a mapping target parameter
*/
public static ParameterBinding forMappingTargetBinding(Type resultType) {
- return new ParameterBinding( resultType, null, true, false, false, false, null );
+ return new ParameterBinding( resultType, null, true, false, false, false, false, null );
}
/**
@@ -161,10 +180,10 @@ public class ParameterBinding {
* @return a parameter binding representing a mapping source type
*/
public static ParameterBinding forSourceTypeBinding(Type sourceType) {
- return new ParameterBinding( sourceType, null, false, false, false, false, null );
+ return new ParameterBinding( sourceType, null, false, false, false, false, false, null );
}
public static ParameterBinding fromSourceRHS(SourceRHS sourceRHS) {
- return new ParameterBinding( sourceRHS.getSourceType(), null, false, false, false, false, sourceRHS );
+ return new ParameterBinding( sourceRHS.getSourceType(), null, false, false, false, false, false, sourceRHS );
}
}
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java
index 37b57d902..42c318ca4 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java
@@ -47,6 +47,7 @@ public class SourceMethod implements Method {
private final List parameters;
private final Parameter mappingTargetParameter;
private final Parameter targetTypeParameter;
+ private final Parameter sourcePropertyNameParameter;
private final Parameter targetPropertyNameParameter;
private final boolean isObjectFactory;
private final boolean isPresenceCheck;
@@ -249,6 +250,7 @@ public class SourceMethod implements Method {
this.mappingTargetParameter = Parameter.getMappingTargetParameter( parameters );
this.targetTypeParameter = Parameter.getTargetTypeParameter( parameters );
+ this.sourcePropertyNameParameter = Parameter.getSourcePropertyNameParameter( parameters );
this.targetPropertyNameParameter = Parameter.getTargetPropertyNameParameter( parameters );
this.hasObjectFactoryAnnotation = ObjectFactoryGem.instanceOn( executable ) != null;
this.isObjectFactory = determineIfIsObjectFactory();
@@ -265,9 +267,10 @@ public class SourceMethod implements Method {
private boolean determineIfIsObjectFactory() {
boolean hasNoSourceParameters = getSourceParameters().isEmpty();
boolean hasNoMappingTargetParam = getMappingTargetParameter() == null;
+ boolean hasNoSourcePropertyNameParam = getSourcePropertyNameParameter() == null;
boolean hasNoTargetPropertyNameParam = getTargetPropertyNameParameter() == null;
return !isLifecycleCallbackMethod() && !returnType.isVoid()
- && hasNoMappingTargetParam && hasNoTargetPropertyNameParam
+ && hasNoMappingTargetParam && hasNoSourcePropertyNameParam && hasNoTargetPropertyNameParam
&& ( hasObjectFactoryAnnotation || hasNoSourceParameters );
}
@@ -382,6 +385,10 @@ public class SourceMethod implements Method {
return targetTypeParameter;
}
+ public Parameter getSourcePropertyNameParameter() {
+ return sourcePropertyNameParameter;
+ }
+
public Parameter getTargetPropertyNameParameter() {
return targetPropertyNameParameter;
}
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionContext.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionContext.java
index 9e82dae25..800278a4c 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionContext.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionContext.java
@@ -171,6 +171,7 @@ public class SelectionContext {
if ( sourceRHS != null ) {
availableParams.addAll( ParameterBinding.fromParameters( method.getParameters() ) );
availableParams.add( ParameterBinding.fromSourceRHS( sourceRHS ) );
+ addSourcePropertyNameBindings( availableParams, sourceRHS.getSourceType(), typeFactory );
}
else {
availableParams.addAll( ParameterBinding.fromParameters( method.getParameters() ) );
@@ -189,6 +190,7 @@ public class SelectionContext {
List availableParams = new ArrayList<>();
availableParams.add( ParameterBinding.forSourceTypeBinding( sourceType ) );
+ addSourcePropertyNameBindings( availableParams, sourceType, typeFactory );
for ( Parameter param : mappingMethod.getParameters() ) {
if ( param.isMappingContext() ) {
@@ -201,6 +203,22 @@ public class SelectionContext {
return availableParams;
}
+ private static void addSourcePropertyNameBindings(List availableParams, Type sourceType,
+ TypeFactory typeFactory) {
+
+ boolean sourcePropertyNameAvailable = false;
+ for ( ParameterBinding pb : availableParams ) {
+ if ( pb.isSourcePropertyName() ) {
+ sourcePropertyNameAvailable = true;
+ break;
+ }
+ }
+ if ( !sourcePropertyNameAvailable ) {
+ availableParams.add( ParameterBinding.forSourcePropertyNameBinding( typeFactory.getType( String.class ) ) );
+ }
+
+ }
+
/**
* Adds default parameter bindings for the mapping-target and target-type if not already available.
*
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TypeSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TypeSelector.java
index 24275f594..50f834d43 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TypeSelector.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TypeSelector.java
@@ -215,6 +215,7 @@ public class TypeSelector implements MethodSelector {
if ( parameter.isTargetType() == candidate.isTargetType()
&& parameter.isMappingTarget() == candidate.isMappingTarget()
&& parameter.isMappingContext() == candidate.isMappingContext()
+ && parameter.isSourcePropertyName() == candidate.isSourcePropertyName()
&& parameter.isTargetPropertyName() == candidate.isTargetPropertyName()) {
result.add( candidate );
}
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 faf7a786f..a9099ea17 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java
@@ -26,6 +26,7 @@ import org.mapstruct.ap.internal.gem.MapMappingGem;
import org.mapstruct.ap.internal.gem.MappingGem;
import org.mapstruct.ap.internal.gem.MappingsGem;
import org.mapstruct.ap.internal.gem.ObjectFactoryGem;
+import org.mapstruct.ap.internal.gem.SourcePropertyNameGem;
import org.mapstruct.ap.internal.gem.SubclassMappingGem;
import org.mapstruct.ap.internal.gem.SubclassMappingsGem;
import org.mapstruct.ap.internal.gem.TargetPropertyNameGem;
@@ -415,6 +416,16 @@ public class MethodRetrievalProcessor implements ModelElementProcessor parameters, Type returnType) {
for ( Parameter param : parameters ) {
+
+ if ( param.isSourcePropertyName() && !param.getType().isString() ) {
+ messager.printMessage(
+ param.getElement(),
+ SourcePropertyNameGem.instanceOn( param.getElement() ).mirror(),
+ Message.RETRIEVAL_SOURCE_PROPERTY_NAME_WRONG_TYPE
+ );
+ return false;
+ }
+
if ( param.isTargetPropertyName() && !param.getType().isString() ) {
messager.printMessage(
param.getElement(),
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java
index 4b4331539..d8f27791e 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java
@@ -177,6 +177,7 @@ public enum Message {
RETRIEVAL_MAPPER_USES_CYCLE( "The mapper %s is referenced itself in Mapper#uses.", Diagnostic.Kind.WARNING ),
RETRIEVAL_AFTER_METHOD_NOT_IMPLEMENTED( "@AfterMapping can only be applied to an implemented method." ),
RETRIEVAL_BEFORE_METHOD_NOT_IMPLEMENTED( "@BeforeMapping can only be applied to an implemented method." ),
+ RETRIEVAL_SOURCE_PROPERTY_NAME_WRONG_TYPE( "@SourcePropertyName can only by applied to a String parameter." ),
RETRIEVAL_TARGET_PROPERTY_NAME_WRONG_TYPE( "@TargetPropertyName can only by applied to a String parameter." ),
INHERITINVERSECONFIGURATION_DUPLICATES( "Several matching inverse methods exist: %s(). Specify a name explicitly." ),
diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReference.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReference.ftl
index 4b45643dc..63f983df4 100644
--- a/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReference.ftl
+++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReference.ftl
@@ -44,6 +44,8 @@
<#if ext.targetBeanName??>${ext.targetBeanName}<#else>${param.variableName}#if><#if ext.targetReadAccessorName??>.${ext.targetReadAccessorName}#if><#t>
<#elseif param.mappingContext>
${param.variableName}<#t>
+ <#elseif param.sourcePropertyName>
+ "${ext.sourcePropertyName}"<#t>
<#elseif param.targetPropertyName>
"${ext.targetPropertyName}"<#t>
<#elseif param.sourceRHS??>
@@ -60,7 +62,7 @@
#macro>
<#--
macro: assignment
- purpose: note: takes its targetyType from the singleSourceParameterType
+ purpose: note: takes its targetType from the singleSourceParameterType
-->
<#macro _assignment assignmentToUse>
<@includeModel object=assignmentToUse
@@ -69,6 +71,7 @@
existingInstanceMapping=ext.existingInstanceMapping
targetReadAccessorName=ext.targetReadAccessorName
targetWriteAccessorName=ext.targetWriteAccessorName
+ sourcePropertyName=ext.sourcePropertyName
targetPropertyName=ext.targetPropertyName
targetType=singleSourceParameterType/>
-#macro>
\ No newline at end of file
+#macro>
diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReferencePresenceCheck.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReferencePresenceCheck.ftl
index 0452e1699..68b05d84e 100644
--- a/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReferencePresenceCheck.ftl
+++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReferencePresenceCheck.ftl
@@ -8,5 +8,6 @@
<#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.MethodReferencePresenceCheck" -->
<#if isNegate()>!#if><@includeModel object=methodReference
presenceCheck=true
+ sourcePropertyName=ext.sourcePropertyName
targetPropertyName=ext.targetPropertyName
- targetType=ext.targetType/>
\ No newline at end of file
+ targetType=ext.targetType/>
diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/PropertyMapping.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/PropertyMapping.ftl
index e6aef4cec..f45659cb5 100644
--- a/processor/src/main/resources/org/mapstruct/ap/internal/model/PropertyMapping.ftl
+++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/PropertyMapping.ftl
@@ -11,6 +11,7 @@
existingInstanceMapping=ext.existingInstanceMapping
targetReadAccessorName=targetReadAccessorName
targetWriteAccessorName=targetWriteAccessorName
+ sourcePropertyName=sourcePropertyName
targetPropertyName=name
targetType=targetType
defaultValueAssignment=defaultValueAssignment />
\ No newline at end of file
diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/TypeConversion.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/TypeConversion.ftl
index 4a9d356ce..a5e5798d1 100644
--- a/processor/src/main/resources/org/mapstruct/ap/internal/model/TypeConversion.ftl
+++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/TypeConversion.ftl
@@ -14,6 +14,7 @@ ${openExpression}<@_assignment/>${closeExpression}
existingInstanceMapping=ext.existingInstanceMapping
targetReadAccessorName=ext.targetReadAccessorName
targetWriteAccessorName=ext.targetWriteAccessorName
+ sourcePropertyName=ext.sourcePropertyName
targetPropertyName=ext.targetPropertyName
targetType=ext.targetType/>
#macro>
diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl
index bce28ebe1..278b441aa 100644
--- a/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl
+++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/macro/CommonMacros.ftl
@@ -17,6 +17,7 @@
<#if sourcePresenceCheckerReference??>
if ( <@includeModel object=sourcePresenceCheckerReference
targetPropertyName=ext.targetPropertyName
+ sourcePropertyName=ext.sourcePropertyName
targetType=ext.targetType/> ) {
<#nested>
}
@@ -61,6 +62,7 @@
<#if sourcePresenceCheckerReference??>
if ( <@includeModel object=sourcePresenceCheckerReference
targetType=ext.targetType
+ sourcePropertyName=ext.sourcePropertyName
targetPropertyName=ext.targetPropertyName /> ) {
<#if needs_explicit_local_var>
<@includeModel object=nullCheckLocalVarType/> ${nullCheckLocalVarName} = <@lib.handleAssignment/>;
diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/Address.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/Address.java
similarity index 86%
rename from processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/Address.java
rename to processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/Address.java
index 162ed118b..339af7594 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/Address.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/Address.java
@@ -3,7 +3,7 @@
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
-package org.mapstruct.ap.test.conditional.targetpropertyname;
+package org.mapstruct.ap.test.conditional.propertyname;
/**
* @author Nikola Ivačič
diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/AddressDto.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/AddressDto.java
similarity index 86%
rename from processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/AddressDto.java
rename to processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/AddressDto.java
index f4cbc7191..c6a63065d 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/AddressDto.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/AddressDto.java
@@ -3,7 +3,7 @@
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
-package org.mapstruct.ap.test.conditional.targetpropertyname;
+package org.mapstruct.ap.test.conditional.propertyname;
/**
* @author Nikola Ivačič
diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/DomainModel.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/DomainModel.java
similarity index 80%
rename from processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/DomainModel.java
rename to processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/DomainModel.java
index 4d2c716a9..8e5fa8695 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/DomainModel.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/DomainModel.java
@@ -3,7 +3,7 @@
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
-package org.mapstruct.ap.test.conditional.targetpropertyname;
+package org.mapstruct.ap.test.conditional.propertyname;
/**
* Target Property Name test entities
diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/Employee.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/Employee.java
similarity index 96%
rename from processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/Employee.java
rename to processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/Employee.java
index 59ee3426e..497717a59 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/Employee.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/Employee.java
@@ -3,7 +3,7 @@
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
-package org.mapstruct.ap.test.conditional.targetpropertyname;
+package org.mapstruct.ap.test.conditional.propertyname;
import java.util.List;
diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/EmployeeDto.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/EmployeeDto.java
similarity index 75%
rename from processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/EmployeeDto.java
rename to processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/EmployeeDto.java
index 5d81a9334..f49f25e4a 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/EmployeeDto.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/EmployeeDto.java
@@ -3,7 +3,7 @@
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
-package org.mapstruct.ap.test.conditional.targetpropertyname;
+package org.mapstruct.ap.test.conditional.propertyname;
import java.util.List;
@@ -15,14 +15,14 @@ public class EmployeeDto implements DomainModel {
private String firstName;
private String lastName;
private String title;
- private String country;
+ private String originCountry;
private boolean active;
private int age;
private EmployeeDto boss;
private AddressDto primaryAddress;
- private List addresses;
+ private List originAddresses;
public String getFirstName() {
return firstName;
@@ -48,12 +48,12 @@ public class EmployeeDto implements DomainModel {
this.title = title;
}
- public String getCountry() {
- return country;
+ public String getOriginCountry() {
+ return originCountry;
}
- public void setCountry(String country) {
- this.country = country;
+ public void setOriginCountry(String originCountry) {
+ this.originCountry = originCountry;
}
public boolean isActive() {
@@ -88,11 +88,11 @@ public class EmployeeDto implements DomainModel {
this.primaryAddress = primaryAddress;
}
- public List getAddresses() {
- return addresses;
+ public List getOriginAddresses() {
+ return originAddresses;
}
- public void setAddresses(List addresses) {
- this.addresses = addresses;
+ public void setOriginAddresses(List originAddresses) {
+ this.originAddresses = originAddresses;
}
}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodForCollectionMapperWithSourcePropertyName.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodForCollectionMapperWithSourcePropertyName.java
new file mode 100644
index 000000000..944fe5d14
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodForCollectionMapperWithSourcePropertyName.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.conditional.propertyname.sourcepropertyname;
+
+import java.util.Collection;
+
+import org.mapstruct.Condition;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.SourcePropertyName;
+import org.mapstruct.ap.test.conditional.propertyname.Employee;
+import org.mapstruct.ap.test.conditional.propertyname.EmployeeDto;
+import org.mapstruct.factory.Mappers;
+
+/**
+ * @author Nikola Ivačič
+ * @author Mohammad Al Zouabi
+ * @author Oliver Erhart
+ */
+@Mapper
+public interface ConditionalMethodForCollectionMapperWithSourcePropertyName {
+
+ ConditionalMethodForCollectionMapperWithSourcePropertyName INSTANCE
+ = Mappers.getMapper( ConditionalMethodForCollectionMapperWithSourcePropertyName.class );
+
+ @Mapping(target = "country", source = "originCountry")
+ @Mapping(target = "addresses", source = "originAddresses")
+ Employee map(EmployeeDto employee);
+
+ @Condition
+ default boolean isNotEmpty(Collection collection, @SourcePropertyName String propName) {
+ if ( "originAddresses".equalsIgnoreCase( propName ) ) {
+ return false;
+ }
+ return collection != null && !collection.isEmpty();
+ }
+
+ @Condition
+ default boolean isNotBlank(String value, @SourcePropertyName String propName) {
+ if ( propName.equalsIgnoreCase( "originCountry" ) ) {
+ return false;
+ }
+ return value != null && !value.trim().isEmpty();
+ }
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodInMapperWithAllExceptTarget.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodInMapperWithAllExceptTarget.java
new file mode 100644
index 000000000..ea6a77d94
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodInMapperWithAllExceptTarget.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.conditional.propertyname.sourcepropertyname;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import org.mapstruct.Condition;
+import org.mapstruct.Context;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.SourcePropertyName;
+import org.mapstruct.ap.test.conditional.propertyname.DomainModel;
+import org.mapstruct.ap.test.conditional.propertyname.Employee;
+import org.mapstruct.ap.test.conditional.propertyname.EmployeeDto;
+import org.mapstruct.factory.Mappers;
+
+/**
+ * @author Filip Hrisafov
+ * @author Nikola Ivačič
+ * @author Mohammad Al Zouabi
+ * @author Oliver Erhart
+ */
+@Mapper
+public interface ConditionalMethodInMapperWithAllExceptTarget {
+
+ ConditionalMethodInMapperWithAllExceptTarget INSTANCE
+ = Mappers.getMapper( ConditionalMethodInMapperWithAllExceptTarget.class );
+
+ class PresenceUtils {
+ Set visited = new LinkedHashSet<>();
+ Set visitedSources = new LinkedHashSet<>();
+ }
+
+ @Mapping(target = "country", source = "originCountry")
+ @Mapping(target = "addresses", source = "originAddresses")
+ Employee map(EmployeeDto employee, @Context PresenceUtils utils);
+
+ @Condition
+ default boolean isNotBlank(String value,
+ DomainModel source,
+ @SourcePropertyName String propName,
+ @Context PresenceUtils utils) {
+ utils.visited.add( propName );
+ utils.visitedSources.add( source.getClass().getSimpleName() );
+ if ( propName.equalsIgnoreCase( "firstName" ) ) {
+ return true;
+ }
+ return value != null && !value.trim().isEmpty();
+ }
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodInMapperWithAllOptions.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodInMapperWithAllOptions.java
new file mode 100644
index 000000000..ee3be5764
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodInMapperWithAllOptions.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.conditional.propertyname.sourcepropertyname;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import org.mapstruct.Condition;
+import org.mapstruct.Context;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.MappingTarget;
+import org.mapstruct.SourcePropertyName;
+import org.mapstruct.TargetPropertyName;
+import org.mapstruct.ap.test.conditional.propertyname.DomainModel;
+import org.mapstruct.ap.test.conditional.propertyname.Employee;
+import org.mapstruct.ap.test.conditional.propertyname.EmployeeDto;
+import org.mapstruct.factory.Mappers;
+
+/**
+ * @author Filip Hrisafov
+ * @author Nikola Ivačič
+ * @author Mohammad Al Zouabi
+ * @author Oliver Erhart
+ */
+@Mapper
+public interface ConditionalMethodInMapperWithAllOptions {
+
+ ConditionalMethodInMapperWithAllOptions INSTANCE
+ = Mappers.getMapper( ConditionalMethodInMapperWithAllOptions.class );
+
+ class PresenceUtils {
+ Set visitedSourceNames = new LinkedHashSet<>();
+ Set visitedTargetNames = new LinkedHashSet<>();
+ Set visitedSources = new LinkedHashSet<>();
+ Set visitedTargets = new LinkedHashSet<>();
+ }
+
+ @Mapping(target = "country", source = "originCountry")
+ @Mapping(target = "addresses", source = "originAddresses")
+ void map(EmployeeDto employeeDto,
+ @MappingTarget Employee employee,
+ @Context PresenceUtils utils);
+
+ @Condition
+ default boolean isNotBlank(String value,
+ DomainModel source,
+ @MappingTarget DomainModel target,
+ @SourcePropertyName String sourcePropName,
+ @TargetPropertyName String targetPropName,
+ @Context PresenceUtils utils) {
+ utils.visitedSourceNames.add( sourcePropName );
+ utils.visitedTargetNames.add( targetPropName );
+ utils.visitedSources.add( source.getClass().getSimpleName() );
+ utils.visitedTargets.add( target.getClass().getSimpleName() );
+ if ( sourcePropName.equalsIgnoreCase( "lastName" ) ) {
+ return false;
+ }
+ return value != null && !value.trim().isEmpty();
+ }
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodInMapperWithSourcePropertyName.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodInMapperWithSourcePropertyName.java
new file mode 100644
index 000000000..41ff6c526
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodInMapperWithSourcePropertyName.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.conditional.propertyname.sourcepropertyname;
+
+import org.mapstruct.Condition;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.SourcePropertyName;
+import org.mapstruct.ap.test.conditional.propertyname.Employee;
+import org.mapstruct.ap.test.conditional.propertyname.EmployeeDto;
+import org.mapstruct.factory.Mappers;
+
+/**
+ * @author Filip Hrisafov
+ * @author Nikola Ivačič
+ * @author Mohammad Al Zouabi
+ * @author Oliver Erhart
+ */
+@Mapper
+public interface ConditionalMethodInMapperWithSourcePropertyName {
+
+ ConditionalMethodInMapperWithSourcePropertyName INSTANCE
+ = Mappers.getMapper( ConditionalMethodInMapperWithSourcePropertyName.class );
+
+ @Mapping(target = "country", source = "originCountry")
+ @Mapping(target = "addresses", source = "originAddresses")
+ Employee map(EmployeeDto employee);
+
+ @Condition
+ default boolean isNotBlank(String value, @SourcePropertyName String propName) {
+ if ( propName.equalsIgnoreCase( "originCountry" ) ) {
+ return false;
+ }
+ return value != null && !value.trim().isEmpty();
+ }
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodInUsesMapperWithSourcePropertyName.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodInUsesMapperWithSourcePropertyName.java
new file mode 100644
index 000000000..8ba222512
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodInUsesMapperWithSourcePropertyName.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.conditional.propertyname.sourcepropertyname;
+
+import org.mapstruct.Condition;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.SourcePropertyName;
+import org.mapstruct.ap.test.conditional.propertyname.Employee;
+import org.mapstruct.ap.test.conditional.propertyname.EmployeeDto;
+import org.mapstruct.factory.Mappers;
+
+/**
+ * @author Filip Hrisafov
+ * @author Nikola Ivačič
+ * @author Mohammad Al Zouabi
+ * @author Oliver Erhart
+ */
+@Mapper(uses = ConditionalMethodInUsesMapperWithSourcePropertyName.PresenceUtils.class)
+public interface ConditionalMethodInUsesMapperWithSourcePropertyName {
+
+ ConditionalMethodInUsesMapperWithSourcePropertyName INSTANCE
+ = Mappers.getMapper( ConditionalMethodInUsesMapperWithSourcePropertyName.class );
+
+ @Mapping(target = "country", source = "originCountry")
+ @Mapping(target = "addresses", source = "originAddresses")
+ Employee map(EmployeeDto employee);
+
+ class PresenceUtils {
+
+ @Condition
+ public boolean isNotBlank(String value, @SourcePropertyName String propName) {
+ if ( propName.equalsIgnoreCase( "originCountry" ) ) {
+ return false;
+ }
+ return value != null && !value.trim().isEmpty();
+ }
+
+ }
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodWithSourcePropertyNameInContextMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodWithSourcePropertyNameInContextMapper.java
new file mode 100644
index 000000000..64b6fcbba
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ConditionalMethodWithSourcePropertyNameInContextMapper.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.conditional.propertyname.sourcepropertyname;
+
+import java.util.Deque;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import org.mapstruct.AfterMapping;
+import org.mapstruct.BeforeMapping;
+import org.mapstruct.Condition;
+import org.mapstruct.Context;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.SourcePropertyName;
+import org.mapstruct.TargetType;
+import org.mapstruct.ap.test.conditional.propertyname.Address;
+import org.mapstruct.ap.test.conditional.propertyname.AddressDto;
+import org.mapstruct.ap.test.conditional.propertyname.DomainModel;
+import org.mapstruct.ap.test.conditional.propertyname.Employee;
+import org.mapstruct.ap.test.conditional.propertyname.EmployeeDto;
+import org.mapstruct.factory.Mappers;
+
+/**
+ * @author Nikola Ivačič
+ * @author Mohammad Al Zouabi
+ * @author Oliver Erhart
+ */
+@Mapper
+public interface ConditionalMethodWithSourcePropertyNameInContextMapper {
+
+ ConditionalMethodWithSourcePropertyNameInContextMapper INSTANCE
+ = Mappers.getMapper( ConditionalMethodWithSourcePropertyNameInContextMapper.class );
+
+ @Mapping(target = "country", source = "originCountry")
+ @Mapping(target = "addresses", source = "originAddresses")
+ Employee map(EmployeeDto employee, @Context PresenceUtils utils);
+
+ Address map(AddressDto addressDto, @Context PresenceUtils utils);
+
+ class PresenceUtils {
+ Set visited = new LinkedHashSet<>();
+
+ @Condition
+ public boolean isNotBlank(String value, @SourcePropertyName String propName) {
+ visited.add( propName );
+ return value != null && !value.trim().isEmpty();
+ }
+ }
+
+ @Mapping(target = "country", source = "originCountry")
+ @Mapping(target = "addresses", source = "originAddresses")
+ Employee map(EmployeeDto employee, @Context PresenceUtilsAllProps utils);
+
+ Address map(AddressDto addressDto, @Context PresenceUtilsAllProps utils);
+
+ class PresenceUtilsAllProps {
+ Set visited = new LinkedHashSet<>();
+
+ @Condition
+ public boolean collect(@SourcePropertyName String propName) {
+ visited.add( propName );
+ return true;
+ }
+ }
+
+ @Mapping(target = "country", source = "originCountry")
+ @Mapping(target = "addresses", source = "originAddresses")
+ Employee map(EmployeeDto employee, @Context PresenceUtilsAllPropsWithSource utils);
+
+ Address map(AddressDto addressDto, @Context PresenceUtilsAllPropsWithSource utils);
+
+ @BeforeMapping
+ default void before(DomainModel source, @Context PresenceUtilsAllPropsWithSource utils) {
+ String lastProp = utils.visitedSegments.peekLast();
+ if ( lastProp != null && source != null ) {
+ utils.path.offerLast( lastProp );
+ }
+ }
+
+ @AfterMapping
+ default void after(@TargetType Class targetClass, @Context PresenceUtilsAllPropsWithSource utils) {
+ // intermediate method for collection mapping must not change the path
+ if (targetClass != List.class) {
+ utils.path.pollLast();
+ }
+ }
+
+ class PresenceUtilsAllPropsWithSource {
+ Deque visitedSegments = new LinkedList<>();
+ Deque visited = new LinkedList<>();
+ Deque path = new LinkedList<>();
+
+ @Condition
+ public boolean collect(@SourcePropertyName String propName) {
+ visitedSegments.offerLast( propName );
+ path.offerLast( propName );
+ visited.offerLast( String.join( ".", path ) );
+ path.pollLast();
+ return true;
+ }
+ }
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ErroneousNonStringSourcePropertyNameParameter.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ErroneousNonStringSourcePropertyNameParameter.java
new file mode 100644
index 000000000..5ed118676
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/ErroneousNonStringSourcePropertyNameParameter.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.conditional.propertyname.sourcepropertyname;
+
+import org.mapstruct.Condition;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.SourcePropertyName;
+import org.mapstruct.ap.test.conditional.propertyname.Employee;
+import org.mapstruct.ap.test.conditional.propertyname.EmployeeDto;
+
+@Mapper
+public interface ErroneousNonStringSourcePropertyNameParameter {
+
+ @Mapping(target = "country", source = "originCountry")
+ @Mapping(target = "addresses", source = "originAddresses")
+ Employee map(EmployeeDto employee);
+
+ @Condition
+ default boolean isNotBlank(String value, @SourcePropertyName int propName) {
+ return value != null && !value.trim().isEmpty();
+ }
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/SourcePropertyNameTest.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/SourcePropertyNameTest.java
new file mode 100644
index 000000000..2471ed647
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/sourcepropertyname/SourcePropertyNameTest.java
@@ -0,0 +1,312 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.ap.test.conditional.propertyname.sourcepropertyname;
+
+import java.util.Collections;
+
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.mapstruct.ap.test.conditional.propertyname.Address;
+import org.mapstruct.ap.test.conditional.propertyname.AddressDto;
+import org.mapstruct.ap.test.conditional.propertyname.DomainModel;
+import org.mapstruct.ap.test.conditional.propertyname.Employee;
+import org.mapstruct.ap.test.conditional.propertyname.EmployeeDto;
+import org.mapstruct.ap.testutil.IssueKey;
+import org.mapstruct.ap.testutil.ProcessorTest;
+import org.mapstruct.ap.testutil.WithClasses;
+import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult;
+import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic;
+import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome;
+import org.mapstruct.ap.testutil.runner.GeneratedSource;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * @author Filip Hrisafov
+ * @author Nikola Ivačič
+ * @author Mohammad Al Zouabi
+ * @author Oliver Erhart
+ */
+@IssueKey("3323")
+@WithClasses({
+ Address.class,
+ AddressDto.class,
+ Employee.class,
+ EmployeeDto.class,
+ DomainModel.class
+})
+public class SourcePropertyNameTest {
+
+ @RegisterExtension
+ final GeneratedSource generatedSource = new GeneratedSource();
+
+ @ProcessorTest
+ @WithClasses({
+ ConditionalMethodInMapperWithSourcePropertyName.class
+ })
+ public void conditionalMethodInMapperWithSourcePropertyName() {
+ ConditionalMethodInMapperWithSourcePropertyName mapper
+ = ConditionalMethodInMapperWithSourcePropertyName.INSTANCE;
+
+ EmployeeDto employeeDto = new EmployeeDto();
+ employeeDto.setFirstName( " " );
+ employeeDto.setLastName( "Testirovich" );
+ employeeDto.setOriginCountry( "US" );
+ employeeDto.setOriginAddresses(
+ Collections.singletonList( new AddressDto( "Testing St. 6" ) )
+ );
+
+ Employee employee = mapper.map( employeeDto );
+ assertThat( employee.getLastName() ).isEqualTo( "Testirovich" );
+ assertThat( employee.getFirstName() ).isNull();
+ assertThat( employee.getCountry() ).isNull();
+ assertThat( employee.getAddresses() )
+ .extracting( Address::getStreet )
+ .containsExactly( "Testing St. 6" );
+ }
+
+ @ProcessorTest
+ @WithClasses({
+ ConditionalMethodForCollectionMapperWithSourcePropertyName.class
+ })
+ public void conditionalMethodForCollectionMapperWithSourcePropertyName() {
+ ConditionalMethodForCollectionMapperWithSourcePropertyName mapper
+ = ConditionalMethodForCollectionMapperWithSourcePropertyName.INSTANCE;
+
+ EmployeeDto employeeDto = new EmployeeDto();
+ employeeDto.setFirstName( " " );
+ employeeDto.setLastName( "Testirovich" );
+ employeeDto.setOriginCountry( "US" );
+ employeeDto.setOriginAddresses(
+ Collections.singletonList( new AddressDto( "Testing St. 6" ) )
+ );
+
+ Employee employee = mapper.map( employeeDto );
+ assertThat( employee.getLastName() ).isEqualTo( "Testirovich" );
+ assertThat( employee.getFirstName() ).isNull();
+ assertThat( employee.getCountry() ).isNull();
+ assertThat( employee.getAddresses() ).isNull();
+ }
+
+ @ProcessorTest
+ @WithClasses({
+ ConditionalMethodInUsesMapperWithSourcePropertyName.class
+ })
+ public void conditionalMethodInUsesMapperWithSourcePropertyName() {
+ ConditionalMethodInUsesMapperWithSourcePropertyName mapper
+ = ConditionalMethodInUsesMapperWithSourcePropertyName.INSTANCE;
+
+ EmployeeDto employeeDto = new EmployeeDto();
+ employeeDto.setFirstName( " " );
+ employeeDto.setLastName( "Testirovich" );
+ employeeDto.setOriginCountry( "US" );
+ employeeDto.setOriginAddresses(
+ Collections.singletonList( new AddressDto( "Testing St. 6" ) )
+ );
+
+ Employee employee = mapper.map( employeeDto );
+ assertThat( employee.getLastName() ).isEqualTo( "Testirovich" );
+ assertThat( employee.getFirstName() ).isNull();
+ assertThat( employee.getCountry() ).isNull();
+ assertThat( employee.getAddresses() )
+ .extracting( Address::getStreet )
+ .containsExactly( "Testing St. 6" );
+ }
+
+ @ProcessorTest
+ @WithClasses({
+ ConditionalMethodInMapperWithAllOptions.class
+ })
+ public void conditionalMethodInMapperWithAllOptions() {
+ ConditionalMethodInMapperWithAllOptions mapper
+ = ConditionalMethodInMapperWithAllOptions.INSTANCE;
+
+ ConditionalMethodInMapperWithAllOptions.PresenceUtils utils =
+ new ConditionalMethodInMapperWithAllOptions.PresenceUtils();
+
+ EmployeeDto employeeDto = new EmployeeDto();
+ employeeDto.setFirstName( " " );
+ employeeDto.setLastName( "Testirovich" );
+ employeeDto.setOriginCountry( "US" );
+ employeeDto.setOriginAddresses(
+ Collections.singletonList( new AddressDto( "Testing St. 6" ) )
+ );
+
+ Employee employee = new Employee();
+ mapper.map( employeeDto, employee, utils );
+ assertThat( employee.getLastName() ).isNull();
+ assertThat( employee.getFirstName() ).isNull();
+ assertThat( employee.getCountry() ).isEqualTo( "US" );
+ assertThat( employee.getAddresses() )
+ .extracting( Address::getStreet )
+ .containsExactly( "Testing St. 6" );
+ assertThat( utils.visitedSourceNames )
+ .containsExactlyInAnyOrder( "firstName", "lastName", "title", "originCountry" );
+ assertThat( utils.visitedTargetNames )
+ .containsExactlyInAnyOrder( "firstName", "lastName", "title", "country" );
+ assertThat( utils.visitedSources ).containsExactly( "EmployeeDto" );
+ assertThat( utils.visitedTargets ).containsExactly( "Employee" );
+ }
+
+ @ProcessorTest
+ @WithClasses({
+ ConditionalMethodInMapperWithAllExceptTarget.class
+ })
+ public void conditionalMethodInMapperWithAllExceptTarget() {
+ ConditionalMethodInMapperWithAllExceptTarget mapper
+ = ConditionalMethodInMapperWithAllExceptTarget.INSTANCE;
+
+ ConditionalMethodInMapperWithAllExceptTarget.PresenceUtils utils =
+ new ConditionalMethodInMapperWithAllExceptTarget.PresenceUtils();
+
+ EmployeeDto employeeDto = new EmployeeDto();
+ employeeDto.setFirstName( " " );
+ employeeDto.setLastName( "Testirovich" );
+ employeeDto.setOriginCountry( "US" );
+ employeeDto.setOriginAddresses(
+ Collections.singletonList( new AddressDto( "Testing St. 6" ) )
+ );
+
+ Employee employee = mapper.map( employeeDto, utils );
+ assertThat( employee.getLastName() ).isEqualTo( "Testirovich" );
+ assertThat( employee.getFirstName() ).isEqualTo( " " );
+ assertThat( employee.getCountry() ).isEqualTo( "US" );
+ assertThat( employee.getAddresses() )
+ .extracting( Address::getStreet )
+ .containsExactly( "Testing St. 6" );
+ assertThat( utils.visited )
+ .containsExactlyInAnyOrder( "firstName", "lastName", "title", "originCountry", "street" );
+ assertThat( utils.visitedSources ).containsExactlyInAnyOrder( "EmployeeDto", "AddressDto" );
+ }
+
+ @ProcessorTest
+ @WithClasses({
+ ConditionalMethodWithSourcePropertyNameInContextMapper.class
+ })
+ public void conditionalMethodWithSourcePropertyNameInUsesContextMapper() {
+ ConditionalMethodWithSourcePropertyNameInContextMapper mapper
+ = ConditionalMethodWithSourcePropertyNameInContextMapper.INSTANCE;
+
+ ConditionalMethodWithSourcePropertyNameInContextMapper.PresenceUtils utils =
+ new ConditionalMethodWithSourcePropertyNameInContextMapper.PresenceUtils();
+
+ EmployeeDto employeeDto = new EmployeeDto();
+ employeeDto.setLastName( " " );
+ employeeDto.setOriginCountry( "US" );
+ employeeDto.setOriginAddresses(
+ Collections.singletonList( new AddressDto( "Testing St. 6" ) )
+ );
+
+ Employee employee = mapper.map( employeeDto, utils );
+ assertThat( employee.getLastName() ).isNull();
+ assertThat( employee.getCountry() ).isEqualTo( "US" );
+ assertThat( employee.getAddresses() )
+ .extracting( Address::getStreet )
+ .containsExactly( "Testing St. 6" );
+ assertThat( utils.visited )
+ .containsExactlyInAnyOrder( "firstName", "lastName", "title", "originCountry", "street" );
+
+ ConditionalMethodWithSourcePropertyNameInContextMapper.PresenceUtilsAllProps allPropsUtils =
+ new ConditionalMethodWithSourcePropertyNameInContextMapper.PresenceUtilsAllProps();
+
+ employeeDto = new EmployeeDto();
+ employeeDto.setLastName( "Tester" );
+ employeeDto.setOriginCountry( "US" );
+ employeeDto.setOriginAddresses(
+ Collections.singletonList( new AddressDto( "Testing St. 6" ) )
+ );
+
+ employee = mapper.map( employeeDto, allPropsUtils );
+ assertThat( employee.getLastName() ).isEqualTo( "Tester" );
+ assertThat( employee.getCountry() ).isEqualTo( "US" );
+ assertThat( employee.getAddresses() )
+ .extracting( Address::getStreet )
+ .containsExactly( "Testing St. 6" );
+ assertThat( allPropsUtils.visited )
+ .containsExactlyInAnyOrder(
+ "firstName",
+ "lastName",
+ "title",
+ "originCountry",
+ "active",
+ "age",
+ "boss",
+ "primaryAddress",
+ "originAddresses",
+ "street"
+ );
+
+ ConditionalMethodWithSourcePropertyNameInContextMapper.PresenceUtilsAllPropsWithSource allPropsUtilsWithSource =
+ new ConditionalMethodWithSourcePropertyNameInContextMapper.PresenceUtilsAllPropsWithSource();
+
+ EmployeeDto bossEmployeeDto = new EmployeeDto();
+ bossEmployeeDto.setLastName( "Boss Tester" );
+ bossEmployeeDto.setOriginCountry( "US" );
+ bossEmployeeDto.setOriginAddresses( Collections.singletonList( new AddressDto(
+ "Testing St. 10" ) ) );
+
+ employeeDto = new EmployeeDto();
+ employeeDto.setLastName( "Tester" );
+ employeeDto.setOriginCountry( "US" );
+ employeeDto.setBoss( bossEmployeeDto );
+ employeeDto.setOriginAddresses(
+ Collections.singletonList( new AddressDto( "Testing St. 6" ) )
+ );
+
+ employee = mapper.map( employeeDto, allPropsUtilsWithSource );
+ assertThat( employee.getLastName() ).isEqualTo( "Tester" );
+ assertThat( employee.getCountry() ).isEqualTo( "US" );
+ assertThat( employee.getAddresses() ).isNotEmpty();
+ assertThat( employee.getAddresses().get( 0 ).getStreet() ).isEqualTo( "Testing St. 6" );
+ assertThat( employee.getBoss() ).isNotNull();
+ assertThat( employee.getBoss().getCountry() ).isEqualTo( "US" );
+ assertThat( employee.getBoss().getLastName() ).isEqualTo( "Boss Tester" );
+ assertThat( employee.getBoss().getAddresses() )
+ .extracting( Address::getStreet )
+ .containsExactly( "Testing St. 10" );
+ assertThat( allPropsUtilsWithSource.visited )
+ .containsExactly(
+ "originCountry",
+ "originAddresses",
+ "originAddresses.street",
+ "firstName",
+ "lastName",
+ "title",
+ "active",
+ "age",
+ "boss",
+ "boss.originCountry",
+ "boss.originAddresses",
+ "boss.originAddresses.street",
+ "boss.firstName",
+ "boss.lastName",
+ "boss.title",
+ "boss.active",
+ "boss.age",
+ "boss.boss",
+ "boss.primaryAddress",
+ "primaryAddress"
+ );
+ }
+
+ @ProcessorTest
+ @WithClasses({
+ ErroneousNonStringSourcePropertyNameParameter.class
+ })
+ @ExpectedCompilationOutcome(
+ value = CompilationResult.FAILED,
+ diagnostics = {
+ @Diagnostic(
+ kind = javax.tools.Diagnostic.Kind.ERROR,
+ type = ErroneousNonStringSourcePropertyNameParameter.class,
+ line = 23,
+ message = "@SourcePropertyName can only by applied to a String parameter."
+ )
+ }
+ )
+ public void nonStringSourcePropertyNameParameter() {
+
+ }
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodForCollectionMapperWithTargetPropertyName.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ConditionalMethodForCollectionMapperWithTargetPropertyName.java
similarity index 76%
rename from processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodForCollectionMapperWithTargetPropertyName.java
rename to processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ConditionalMethodForCollectionMapperWithTargetPropertyName.java
index 71baa0942..a51a318eb 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodForCollectionMapperWithTargetPropertyName.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ConditionalMethodForCollectionMapperWithTargetPropertyName.java
@@ -3,15 +3,18 @@
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
-package org.mapstruct.ap.test.conditional.targetpropertyname;
+package org.mapstruct.ap.test.conditional.propertyname.targetpropertyname;
+
+import java.util.Collection;
import org.mapstruct.Condition;
import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
import org.mapstruct.TargetPropertyName;
+import org.mapstruct.ap.test.conditional.propertyname.Employee;
+import org.mapstruct.ap.test.conditional.propertyname.EmployeeDto;
import org.mapstruct.factory.Mappers;
-import java.util.Collection;
-
/**
* @author Nikola Ivačič
*/
@@ -21,6 +24,8 @@ public interface ConditionalMethodForCollectionMapperWithTargetPropertyName {
ConditionalMethodForCollectionMapperWithTargetPropertyName INSTANCE
= Mappers.getMapper( ConditionalMethodForCollectionMapperWithTargetPropertyName.class );
+ @Mapping(target = "country", source = "originCountry")
+ @Mapping(target = "addresses", source = "originAddresses")
Employee map(EmployeeDto employee);
@Condition
diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodInMapperWithAllExceptTarget.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ConditionalMethodInMapperWithAllExceptTarget.java
similarity index 76%
rename from processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodInMapperWithAllExceptTarget.java
rename to processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ConditionalMethodInMapperWithAllExceptTarget.java
index 430303d06..1d0dd3fa5 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodInMapperWithAllExceptTarget.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ConditionalMethodInMapperWithAllExceptTarget.java
@@ -3,17 +3,21 @@
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
-package org.mapstruct.ap.test.conditional.targetpropertyname;
+package org.mapstruct.ap.test.conditional.propertyname.targetpropertyname;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
import org.mapstruct.Condition;
import org.mapstruct.Context;
import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
import org.mapstruct.TargetPropertyName;
+import org.mapstruct.ap.test.conditional.propertyname.DomainModel;
+import org.mapstruct.ap.test.conditional.propertyname.Employee;
+import org.mapstruct.ap.test.conditional.propertyname.EmployeeDto;
import org.mapstruct.factory.Mappers;
-import java.util.LinkedHashSet;
-import java.util.Set;
-
/**
* @author Filip Hrisafov
* @author Nikola Ivačič
@@ -29,6 +33,8 @@ public interface ConditionalMethodInMapperWithAllExceptTarget {
Set visitedSources = new LinkedHashSet<>();
}
+ @Mapping(target = "country", source = "originCountry")
+ @Mapping(target = "addresses", source = "originAddresses")
Employee map(EmployeeDto employee, @Context PresenceUtils utils);
@Condition
diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodInMapperWithAllOptions.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ConditionalMethodInMapperWithAllOptions.java
similarity index 61%
rename from processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodInMapperWithAllOptions.java
rename to processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ConditionalMethodInMapperWithAllOptions.java
index a18ba0db7..d3ccb9ba2 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodInMapperWithAllOptions.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ConditionalMethodInMapperWithAllOptions.java
@@ -3,18 +3,23 @@
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
-package org.mapstruct.ap.test.conditional.targetpropertyname;
+package org.mapstruct.ap.test.conditional.propertyname.targetpropertyname;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
import org.mapstruct.Condition;
import org.mapstruct.Context;
import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
+import org.mapstruct.SourcePropertyName;
import org.mapstruct.TargetPropertyName;
+import org.mapstruct.ap.test.conditional.propertyname.DomainModel;
+import org.mapstruct.ap.test.conditional.propertyname.Employee;
+import org.mapstruct.ap.test.conditional.propertyname.EmployeeDto;
import org.mapstruct.factory.Mappers;
-import java.util.LinkedHashSet;
-import java.util.Set;
-
/**
* @author Filip Hrisafov
* @author Nikola Ivačič
@@ -26,11 +31,14 @@ public interface ConditionalMethodInMapperWithAllOptions {
= Mappers.getMapper( ConditionalMethodInMapperWithAllOptions.class );
class PresenceUtils {
- Set visited = new LinkedHashSet<>();
+ Set visitedSourceNames = new LinkedHashSet<>();
+ Set visitedTargetNames = new LinkedHashSet<>();
Set visitedSources = new LinkedHashSet<>();
Set visitedTargets = new LinkedHashSet<>();
}
+ @Mapping(target = "country", source = "originCountry")
+ @Mapping(target = "addresses", source = "originAddresses")
void map(EmployeeDto employeeDto,
@MappingTarget Employee employee,
@Context PresenceUtils utils);
@@ -39,12 +47,14 @@ public interface ConditionalMethodInMapperWithAllOptions {
default boolean isNotBlank(String value,
DomainModel source,
@MappingTarget DomainModel target,
- @TargetPropertyName String propName,
+ @SourcePropertyName String sourcePropName,
+ @TargetPropertyName String targetPropName,
@Context PresenceUtils utils) {
- utils.visited.add( propName );
+ utils.visitedSourceNames.add( sourcePropName );
+ utils.visitedTargetNames.add( targetPropName );
utils.visitedSources.add( source.getClass().getSimpleName() );
utils.visitedTargets.add( target.getClass().getSimpleName() );
- if ( propName.equalsIgnoreCase( "lastName" ) ) {
+ if ( targetPropName.equalsIgnoreCase( "lastName" ) ) {
return false;
}
return value != null && !value.trim().isEmpty();
diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodInMapperWithTargetPropertyName.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ConditionalMethodInMapperWithTargetPropertyName.java
similarity index 70%
rename from processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodInMapperWithTargetPropertyName.java
rename to processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ConditionalMethodInMapperWithTargetPropertyName.java
index 6d27bc903..d5dc378f3 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodInMapperWithTargetPropertyName.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ConditionalMethodInMapperWithTargetPropertyName.java
@@ -3,11 +3,14 @@
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
-package org.mapstruct.ap.test.conditional.targetpropertyname;
+package org.mapstruct.ap.test.conditional.propertyname.targetpropertyname;
import org.mapstruct.Condition;
import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
import org.mapstruct.TargetPropertyName;
+import org.mapstruct.ap.test.conditional.propertyname.Employee;
+import org.mapstruct.ap.test.conditional.propertyname.EmployeeDto;
import org.mapstruct.factory.Mappers;
/**
@@ -20,6 +23,8 @@ public interface ConditionalMethodInMapperWithTargetPropertyName {
ConditionalMethodInMapperWithTargetPropertyName INSTANCE
= Mappers.getMapper( ConditionalMethodInMapperWithTargetPropertyName.class );
+ @Mapping(target = "country", source = "originCountry")
+ @Mapping(target = "addresses", source = "originAddresses")
Employee map(EmployeeDto employee);
@Condition
diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodInUsesMapperWithTargetPropertyName.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ConditionalMethodInUsesMapperWithTargetPropertyName.java
similarity index 74%
rename from processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodInUsesMapperWithTargetPropertyName.java
rename to processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ConditionalMethodInUsesMapperWithTargetPropertyName.java
index 7c526884e..381b99d4f 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodInUsesMapperWithTargetPropertyName.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ConditionalMethodInUsesMapperWithTargetPropertyName.java
@@ -3,11 +3,14 @@
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
-package org.mapstruct.ap.test.conditional.targetpropertyname;
+package org.mapstruct.ap.test.conditional.propertyname.targetpropertyname;
import org.mapstruct.Condition;
import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
import org.mapstruct.TargetPropertyName;
+import org.mapstruct.ap.test.conditional.propertyname.Employee;
+import org.mapstruct.ap.test.conditional.propertyname.EmployeeDto;
import org.mapstruct.factory.Mappers;
/**
@@ -20,6 +23,8 @@ public interface ConditionalMethodInUsesMapperWithTargetPropertyName {
ConditionalMethodInUsesMapperWithTargetPropertyName INSTANCE
= Mappers.getMapper( ConditionalMethodInUsesMapperWithTargetPropertyName.class );
+ @Mapping(target = "country", source = "originCountry")
+ @Mapping(target = "addresses", source = "originAddresses")
Employee map(EmployeeDto employee);
class PresenceUtils {
diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodWithTargetPropertyNameInContextMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ConditionalMethodWithTargetPropertyNameInContextMapper.java
similarity index 70%
rename from processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodWithTargetPropertyNameInContextMapper.java
rename to processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ConditionalMethodWithTargetPropertyNameInContextMapper.java
index a7cde220c..44bc26243 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ConditionalMethodWithTargetPropertyNameInContextMapper.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ConditionalMethodWithTargetPropertyNameInContextMapper.java
@@ -3,21 +3,29 @@
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
-package org.mapstruct.ap.test.conditional.targetpropertyname;
+package org.mapstruct.ap.test.conditional.propertyname.targetpropertyname;
+
+import java.util.Deque;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
import org.mapstruct.AfterMapping;
import org.mapstruct.BeforeMapping;
import org.mapstruct.Condition;
import org.mapstruct.Context;
import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
import org.mapstruct.TargetPropertyName;
+import org.mapstruct.TargetType;
+import org.mapstruct.ap.test.conditional.propertyname.Address;
+import org.mapstruct.ap.test.conditional.propertyname.AddressDto;
+import org.mapstruct.ap.test.conditional.propertyname.DomainModel;
+import org.mapstruct.ap.test.conditional.propertyname.Employee;
+import org.mapstruct.ap.test.conditional.propertyname.EmployeeDto;
import org.mapstruct.factory.Mappers;
-import java.util.Deque;
-import java.util.LinkedHashSet;
-import java.util.LinkedList;
-import java.util.Set;
-
/**
* @author Nikola Ivačič
*/
@@ -27,6 +35,8 @@ public interface ConditionalMethodWithTargetPropertyNameInContextMapper {
ConditionalMethodWithTargetPropertyNameInContextMapper INSTANCE
= Mappers.getMapper( ConditionalMethodWithTargetPropertyNameInContextMapper.class );
+ @Mapping(target = "country", source = "originCountry")
+ @Mapping(target = "addresses", source = "originAddresses")
Employee map(EmployeeDto employee, @Context PresenceUtils utils);
Address map(AddressDto addressDto, @Context PresenceUtils utils);
@@ -41,6 +51,8 @@ public interface ConditionalMethodWithTargetPropertyNameInContextMapper {
}
}
+ @Mapping(target = "country", source = "originCountry")
+ @Mapping(target = "addresses", source = "originAddresses")
Employee map(EmployeeDto employee, @Context PresenceUtilsAllProps utils);
Address map(AddressDto addressDto, @Context PresenceUtilsAllProps utils);
@@ -55,6 +67,8 @@ public interface ConditionalMethodWithTargetPropertyNameInContextMapper {
}
}
+ @Mapping(target = "country", source = "originCountry")
+ @Mapping(target = "addresses", source = "originAddresses")
Employee map(EmployeeDto employee, @Context PresenceUtilsAllPropsWithSource utils);
Address map(AddressDto addressDto, @Context PresenceUtilsAllPropsWithSource utils);
@@ -68,8 +82,11 @@ public interface ConditionalMethodWithTargetPropertyNameInContextMapper {
}
@AfterMapping
- default void after(@Context PresenceUtilsAllPropsWithSource utils) {
- utils.path.pollLast();
+ default void after(@TargetType Class targetClass, @Context PresenceUtilsAllPropsWithSource utils) {
+ // intermediate method for collection mapping must not change the path
+ if (targetClass != List.class) {
+ utils.path.pollLast();
+ }
}
class PresenceUtilsAllPropsWithSource {
diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ErroneousNonStringTargetPropertyNameParameter.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ErroneousNonStringTargetPropertyNameParameter.java
similarity index 59%
rename from processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ErroneousNonStringTargetPropertyNameParameter.java
rename to processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ErroneousNonStringTargetPropertyNameParameter.java
index ec545a058..d56277abf 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/ErroneousNonStringTargetPropertyNameParameter.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/ErroneousNonStringTargetPropertyNameParameter.java
@@ -3,15 +3,20 @@
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
-package org.mapstruct.ap.test.conditional.targetpropertyname;
+package org.mapstruct.ap.test.conditional.propertyname.targetpropertyname;
import org.mapstruct.Condition;
import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
import org.mapstruct.TargetPropertyName;
+import org.mapstruct.ap.test.conditional.propertyname.Employee;
+import org.mapstruct.ap.test.conditional.propertyname.EmployeeDto;
@Mapper
public interface ErroneousNonStringTargetPropertyNameParameter {
+ @Mapping(target = "country", source = "originCountry")
+ @Mapping(target = "addresses", source = "originAddresses")
Employee map(EmployeeDto employee);
@Condition
diff --git a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/TargetPropertyNameTest.java b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/TargetPropertyNameTest.java
similarity index 88%
rename from processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/TargetPropertyNameTest.java
rename to processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/TargetPropertyNameTest.java
index 91e4b77de..bb90c0b06 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/conditional/targetpropertyname/TargetPropertyNameTest.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/conditional/propertyname/targetpropertyname/TargetPropertyNameTest.java
@@ -3,9 +3,16 @@
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
-package org.mapstruct.ap.test.conditional.targetpropertyname;
+package org.mapstruct.ap.test.conditional.propertyname.targetpropertyname;
+
+import java.util.Collections;
import org.junit.jupiter.api.extension.RegisterExtension;
+import org.mapstruct.ap.test.conditional.propertyname.Address;
+import org.mapstruct.ap.test.conditional.propertyname.AddressDto;
+import org.mapstruct.ap.test.conditional.propertyname.DomainModel;
+import org.mapstruct.ap.test.conditional.propertyname.Employee;
+import org.mapstruct.ap.test.conditional.propertyname.EmployeeDto;
import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.ProcessorTest;
import org.mapstruct.ap.testutil.WithClasses;
@@ -14,8 +21,6 @@ import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic;
import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome;
import org.mapstruct.ap.testutil.runner.GeneratedSource;
-import java.util.Collections;
-
import static org.assertj.core.api.Assertions.assertThat;
/**
@@ -46,8 +51,8 @@ public class TargetPropertyNameTest {
EmployeeDto employeeDto = new EmployeeDto();
employeeDto.setFirstName( " " );
employeeDto.setLastName( "Testirovich" );
- employeeDto.setCountry( "US" );
- employeeDto.setAddresses(
+ employeeDto.setOriginCountry( "US" );
+ employeeDto.setOriginAddresses(
Collections.singletonList( new AddressDto( "Testing St. 6" ) )
);
@@ -71,8 +76,8 @@ public class TargetPropertyNameTest {
EmployeeDto employeeDto = new EmployeeDto();
employeeDto.setFirstName( " " );
employeeDto.setLastName( "Testirovich" );
- employeeDto.setCountry( "US" );
- employeeDto.setAddresses(
+ employeeDto.setOriginCountry( "US" );
+ employeeDto.setOriginAddresses(
Collections.singletonList( new AddressDto( "Testing St. 6" ) )
);
@@ -94,8 +99,8 @@ public class TargetPropertyNameTest {
EmployeeDto employeeDto = new EmployeeDto();
employeeDto.setFirstName( " " );
employeeDto.setLastName( "Testirovich" );
- employeeDto.setCountry( "US" );
- employeeDto.setAddresses(
+ employeeDto.setOriginCountry( "US" );
+ employeeDto.setOriginAddresses(
Collections.singletonList( new AddressDto( "Testing St. 6" ) )
);
@@ -122,8 +127,8 @@ public class TargetPropertyNameTest {
EmployeeDto employeeDto = new EmployeeDto();
employeeDto.setFirstName( " " );
employeeDto.setLastName( "Testirovich" );
- employeeDto.setCountry( "US" );
- employeeDto.setAddresses(
+ employeeDto.setOriginCountry( "US" );
+ employeeDto.setOriginAddresses(
Collections.singletonList( new AddressDto( "Testing St. 6" ) )
);
@@ -135,7 +140,9 @@ public class TargetPropertyNameTest {
assertThat( employee.getAddresses() )
.extracting( Address::getStreet )
.containsExactly( "Testing St. 6" );
- assertThat( utils.visited )
+ assertThat( utils.visitedSourceNames )
+ .containsExactlyInAnyOrder( "firstName", "lastName", "title", "originCountry" );
+ assertThat( utils.visitedTargetNames )
.containsExactlyInAnyOrder( "firstName", "lastName", "title", "country" );
assertThat( utils.visitedSources ).containsExactly( "EmployeeDto" );
assertThat( utils.visitedTargets ).containsExactly( "Employee" );
@@ -155,8 +162,8 @@ public class TargetPropertyNameTest {
EmployeeDto employeeDto = new EmployeeDto();
employeeDto.setFirstName( " " );
employeeDto.setLastName( "Testirovich" );
- employeeDto.setCountry( "US" );
- employeeDto.setAddresses(
+ employeeDto.setOriginCountry( "US" );
+ employeeDto.setOriginAddresses(
Collections.singletonList( new AddressDto( "Testing St. 6" ) )
);
@@ -185,8 +192,8 @@ public class TargetPropertyNameTest {
EmployeeDto employeeDto = new EmployeeDto();
employeeDto.setLastName( " " );
- employeeDto.setCountry( "US" );
- employeeDto.setAddresses(
+ employeeDto.setOriginCountry( "US" );
+ employeeDto.setOriginAddresses(
Collections.singletonList( new AddressDto( "Testing St. 6" ) )
);
@@ -204,8 +211,8 @@ public class TargetPropertyNameTest {
employeeDto = new EmployeeDto();
employeeDto.setLastName( "Tester" );
- employeeDto.setCountry( "US" );
- employeeDto.setAddresses(
+ employeeDto.setOriginCountry( "US" );
+ employeeDto.setOriginAddresses(
Collections.singletonList( new AddressDto( "Testing St. 6" ) )
);
@@ -234,15 +241,15 @@ public class TargetPropertyNameTest {
EmployeeDto bossEmployeeDto = new EmployeeDto();
bossEmployeeDto.setLastName( "Boss Tester" );
- bossEmployeeDto.setCountry( "US" );
- bossEmployeeDto.setAddresses( Collections.singletonList( new AddressDto(
+ bossEmployeeDto.setOriginCountry( "US" );
+ bossEmployeeDto.setOriginAddresses( Collections.singletonList( new AddressDto(
"Testing St. 10" ) ) );
employeeDto = new EmployeeDto();
employeeDto.setLastName( "Tester" );
- employeeDto.setCountry( "US" );
+ employeeDto.setOriginCountry( "US" );
employeeDto.setBoss( bossEmployeeDto );
- employeeDto.setAddresses(
+ employeeDto.setOriginAddresses(
Collections.singletonList( new AddressDto( "Testing St. 6" ) )
);
@@ -259,26 +266,26 @@ public class TargetPropertyNameTest {
.containsExactly( "Testing St. 10" );
assertThat( allPropsUtilsWithSource.visited )
.containsExactly(
+ "country",
+ "addresses",
+ "addresses.street",
"firstName",
"lastName",
"title",
- "country",
"active",
"age",
"boss",
+ "boss.country",
+ "boss.addresses",
+ "boss.addresses.street",
"boss.firstName",
"boss.lastName",
"boss.title",
- "boss.country",
"boss.active",
"boss.age",
"boss.boss",
"boss.primaryAddress",
- "boss.addresses",
- "boss.addresses.street",
- "primaryAddress",
- "addresses",
- "addresses.street"
+ "primaryAddress"
);
}
@@ -293,7 +300,7 @@ public class TargetPropertyNameTest {
@Diagnostic(
kind = javax.tools.Diagnostic.Kind.ERROR,
type = ErroneousNonStringTargetPropertyNameParameter.class,
- line = 18,
+ line = 23,
message = "@TargetPropertyName can only by applied to a String parameter."
)
}