diff --git a/core-jdk8/src/main/java/org/mapstruct/Mapping.java b/core-jdk8/src/main/java/org/mapstruct/Mapping.java
index f329b3a56..53b8799e2 100644
--- a/core-jdk8/src/main/java/org/mapstruct/Mapping.java
+++ b/core-jdk8/src/main/java/org/mapstruct/Mapping.java
@@ -63,6 +63,7 @@ public @interface Mapping {
* This may either be a simple property name (e.g. "address") or a dot-separated property path (e.g. "address.city"
* or "address.city.name"). In case the annotated method has several source parameters, the property name must
* qualified with the parameter name, e.g. "addressParam.city".
+ *
When no matching property is found, MapStruct looks for a matching parameter name instead.
* When used to map an enum constant, the name of the constant member is to be given.
*
* Either this attribute or {@link #constant()} or {@link #expression()} may be specified for a given mapping.
diff --git a/core/src/main/java/org/mapstruct/Mapping.java b/core/src/main/java/org/mapstruct/Mapping.java
index 073a330da..8d09bf30d 100644
--- a/core/src/main/java/org/mapstruct/Mapping.java
+++ b/core/src/main/java/org/mapstruct/Mapping.java
@@ -61,6 +61,7 @@ public @interface Mapping {
* This may either be a simple property name (e.g. "address") or a dot-separated property path (e.g. "address.city"
* or "address.city.name"). In case the annotated method has several source parameters, the property name must
* qualified with the parameter name, e.g. "addressParam.city".
+ * When no matching property is found, MapStruct looks for a matching parameter name instead.
* When used to map an enum constant, the name of the constant member is to be given.
*
* Either this attribute or {@link #constant()} or {@link #expression()} may be specified for a given mapping.
diff --git a/processor/src/main/java/org/mapstruct/ap/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/model/BeanMappingMethod.java
index 59d816d25..401fd9e40 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/BeanMappingMethod.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/BeanMappingMethod.java
@@ -65,6 +65,7 @@ public class BeanMappingMethod extends MappingMethod {
private SourceMethod method;
private Map unprocessedTargetProperties;
private final List propertyMappings = new ArrayList();
+ private final Set unprocessedSourceParameters = new HashSet();
public Builder mappingContext(MappingBuilderContext mappingContext) {
this.ctx = mappingContext;
@@ -74,6 +75,9 @@ public class BeanMappingMethod extends MappingMethod {
public Builder souceMethod(SourceMethod sourceMethod) {
this.method = sourceMethod;
this.unprocessedTargetProperties = initTargetPropertyAccessors();
+ for ( Parameter sourceParameter : method.getSourceParameters() ) {
+ unprocessedSourceParameters.add( sourceParameter );
+ }
return this;
}
@@ -87,6 +91,9 @@ public class BeanMappingMethod extends MappingMethod {
// map properties without a mapping
applyPropertyNameBasedMapping();
+ // map parameters without a mapping
+ applyParameterNameBasedMapping();
+
// report errors on unmapped properties
reportErrorForUnmappedTargetPropertiesIfRequired();
@@ -207,6 +214,7 @@ public class BeanMappingMethod extends MappingMethod {
.dateFormat( mapping.getDateFormat() )
.build();
handledTargets.add( mapping.getTargetName() );
+ unprocessedSourceParameters.remove( sourceRef.getParameter() );
}
}
else {
@@ -279,6 +287,10 @@ public class BeanMappingMethod extends MappingMethod {
for ( Parameter sourceParameter : method.getSourceParameters() ) {
+ if ( sourceParameter.getType().isPrimitive() ) {
+ continue;
+ }
+
for ( ExecutableElement sourceAccessor : sourceParameter.getType().getGetters() ) {
String sourcePropertyName = Executables.getPropertyName( sourceAccessor );
@@ -309,6 +321,8 @@ public class BeanMappingMethod extends MappingMethod {
.dateFormat( mapping != null ? mapping.getDateFormat() : null )
.build();
+ unprocessedSourceParameters.remove( sourceParameter );
+
}
// candidates are handled
candidates.clear();
@@ -337,6 +351,46 @@ public class BeanMappingMethod extends MappingMethod {
}
}
+ private void applyParameterNameBasedMapping() {
+
+ Iterator> targetProperties =
+ unprocessedTargetProperties.entrySet().iterator();
+
+ while ( targetProperties.hasNext() ) {
+
+ Entry targetProperty = targetProperties.next();
+
+ Iterator sourceParameters = unprocessedSourceParameters.iterator();
+
+ while ( sourceParameters.hasNext() ) {
+
+ Parameter sourceParameter = sourceParameters.next();
+ if ( sourceParameter.getName().equals( targetProperty.getKey() ) ) {
+ Mapping mapping = method.getSingleMappingByTargetPropertyName( targetProperty.getKey() );
+
+ SourceReference sourceRef = new SourceReference.BuilderFromProperty()
+ .sourceParameter( sourceParameter )
+ .name( targetProperty.getKey() )
+ .build();
+
+ PropertyMapping propertyMapping = new PropertyMappingBuilder()
+ .mappingContext( ctx )
+ .souceMethod( method )
+ .targetAccessor( targetProperty.getValue() )
+ .targetPropertyName( targetProperty.getKey() )
+ .sourceReference( sourceRef )
+ .qualifiers( mapping != null ? mapping.getQualifiers() : null )
+ .dateFormat( mapping != null ? mapping.getDateFormat() : null )
+ .build();
+
+ propertyMappings.add( propertyMapping );
+ targetProperties.remove();
+ sourceParameters.remove();
+ }
+ }
+ }
+ }
+
private ExecutableElement getSourceAccessor(String sourcePropertyName, List candidates) {
if ( candidates.isEmpty() ) {
return null;
@@ -462,6 +516,28 @@ public class BeanMappingMethod extends MappingMethod {
return types;
}
+ public List getSourceParametersExcludingPrimitives() {
+ List sourceParameters = new ArrayList();
+ for ( Parameter sourceParam : getSourceParameters() ) {
+ if (!sourceParam.getType().isPrimitive()) {
+ sourceParameters.add( sourceParam );
+ }
+ }
+
+ return sourceParameters;
+ }
+
+ public List getSourcePrimitiveParameters() {
+ List sourceParameters = new ArrayList();
+ for ( Parameter sourceParam : getSourceParameters() ) {
+ if (sourceParam.getType().isPrimitive()) {
+ sourceParameters.add( sourceParam );
+ }
+ }
+ return sourceParameters;
+ }
+
+
public MethodReference getFactoryMethod() {
return this.factoryMethod;
}
diff --git a/processor/src/main/java/org/mapstruct/ap/model/common/Parameter.java b/processor/src/main/java/org/mapstruct/ap/model/common/Parameter.java
index 8079d6449..536852dde 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/common/Parameter.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/common/Parameter.java
@@ -71,4 +71,27 @@ public class Parameter extends ModelElement {
public boolean isTargetType() {
return targetType;
}
+
+ @Override
+ public int hashCode() {
+ int hash = 5;
+ hash = 23 * hash + (this.name != null ? this.name.hashCode() : 0);
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if ( obj == null ) {
+ return false;
+ }
+ if ( getClass() != obj.getClass() ) {
+ return false;
+ }
+ final Parameter other = (Parameter) obj;
+ if ( (this.name == null) ? (other.name != null) : !this.name.equals( other.name ) ) {
+ return false;
+ }
+ return true;
+ }
+
}
diff --git a/processor/src/main/java/org/mapstruct/ap/model/source/SourceReference.java b/processor/src/main/java/org/mapstruct/ap/model/source/SourceReference.java
index fe4dace1d..8e6ee2bda 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/source/SourceReference.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/source/SourceReference.java
@@ -234,7 +234,10 @@ public class SourceReference {
}
public SourceReference build() {
- List sourcePropertyEntries = Arrays.asList( new PropertyEntry( name, accessor, type ) );
+ List sourcePropertyEntries = new ArrayList();
+ if ( accessor != null ) {
+ sourcePropertyEntries.add( new PropertyEntry( name, accessor, type ) );
+ }
return new SourceReference( sourceParameter, sourcePropertyEntries, true );
}
}
diff --git a/processor/src/main/resources/org.mapstruct.ap.model.BeanMappingMethod.ftl b/processor/src/main/resources/org.mapstruct.ap.model.BeanMappingMethod.ftl
index eb98b7ba6..2db9a52e9 100644
--- a/processor/src/main/resources/org.mapstruct.ap.model.BeanMappingMethod.ftl
+++ b/processor/src/main/resources/org.mapstruct.ap.model.BeanMappingMethod.ftl
@@ -20,13 +20,13 @@
-->
@Override
<#lt>${accessibility.keyword} <@includeModel object=returnType/> ${name}(<#list parameters as param><@includeModel object=param/><#if param_has_next>, #if>#list>) <@throws/> {
- if ( <#list sourceParameters as sourceParam>${sourceParam.name} == null<#if sourceParam_has_next> && #if>#list> ) {
+ if ( <#list sourceParametersExcludingPrimitives as sourceParam>${sourceParam.name} == null<#if sourceParam_has_next> && #if>#list> ) {
return<#if returnType.name != "void"> null#if>;
}
<#if !existingInstanceMapping><@includeModel object=resultType/> ${resultName} = <#if factoryMethod??><@includeModel object=factoryMethod targetType=resultType raw=true/><#else>new <@includeModel object=resultType/>()#if>;#if>
<#if (sourceParameters?size > 1)>
- <#list sourceParameters as sourceParam>
+ <#list sourceParametersExcludingPrimitives as sourceParam>
<#if (propertyMappingsByParameter[sourceParam.name]?size > 0)>
if ( ${sourceParam.name} != null ) {
<#list propertyMappingsByParameter[sourceParam.name] as propertyMapping>
@@ -35,6 +35,13 @@
}
#if>
#list>
+ <#list sourcePrimitiveParameters as sourceParam>
+ <#if (propertyMappingsByParameter[sourceParam.name]?size > 0)>
+ <#list propertyMappingsByParameter[sourceParam.name] as propertyMapping>
+ <@includeModel object=propertyMapping targetBeanName=resultName existingInstanceMapping=existingInstanceMapping/>
+ #list>
+ #if>
+ #list>
<#else>
<#list propertyMappingsByParameter[sourceParameters[0].name] as propertyMapping>
<@includeModel object=propertyMapping targetBeanName=resultName existingInstanceMapping=existingInstanceMapping/>
diff --git a/processor/src/test/java/org/mapstruct/ap/test/severalsources/SeveralSourceParametersTest.java b/processor/src/test/java/org/mapstruct/ap/test/severalsources/SeveralSourceParametersTest.java
index 8b603207b..14ee7cd53 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/severalsources/SeveralSourceParametersTest.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/severalsources/SeveralSourceParametersTest.java
@@ -98,6 +98,21 @@ public class SeveralSourceParametersTest {
assertThat( deliveryAddress ).isNull();
}
+ @Test
+ @WithClasses({ Person.class, Address.class, DeliveryAddress.class, SourceTargetMapper.class })
+ public void shouldMapSeveralSourceAttributesAndParameters() {
+ Person person = new Person( "Bob", "Garner", 181, "An actor" );
+
+ DeliveryAddress deliveryAddress =
+ SourceTargetMapper.INSTANCE.personAndAddressToDeliveryAddress( person, 42, 12345, "Main street" );
+
+ assertThat( deliveryAddress.getLastName() ).isEqualTo( "Garner" );
+ assertThat( deliveryAddress.getZipCode() ).isEqualTo( 12345 );
+ assertThat( deliveryAddress.getHouseNumber() ).isEqualTo( 42 );
+ assertThat( deliveryAddress.getDescription() ).isEqualTo( "An actor" );
+ assertThat( deliveryAddress.getStreet()).isEqualTo( "Main street" );
+ }
+
@Test
@WithClasses({ ErroneousSourceTargetMapper.class, Address.class, DeliveryAddress.class })
@ProcessorOption(name = "mapstruct.unmappedTargetPolicy", value = "IGNORE")
diff --git a/processor/src/test/java/org/mapstruct/ap/test/severalsources/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/severalsources/SourceTargetMapper.java
index 4d9622eae..9b9e28200 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/severalsources/SourceTargetMapper.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/severalsources/SourceTargetMapper.java
@@ -41,4 +41,9 @@ public interface SourceTargetMapper {
})
void personAndAddressToDeliveryAddress(Person person, Address address,
@MappingTarget DeliveryAddress deliveryAddress);
+
+ @Mapping( target = "description", source = "person.description")
+ DeliveryAddress personAndAddressToDeliveryAddress(Person person, Integer houseNumber, int zipCode,
+ String street);
+
}