diff --git a/core/src/main/java/org/mapstruct/MappingTarget.java b/core/src/main/java/org/mapstruct/MappingTarget.java
new file mode 100644
index 000000000..6d689b39f
--- /dev/null
+++ b/core/src/main/java/org/mapstruct/MappingTarget.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright 2012-2013 Gunnar Morling (http://www.gunnarmorling.de/)
+ * and/or other contributors as indicated by the @authors tag. See the
+ * copyright.txt file in the distribution for a full listing of all
+ * contributors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mapstruct;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * Declares a parameter of a mapping method to be the target of the mapping.
+ *
+ * Not more than one parameter can be declared as {@code MappingTarget}.
+ *
+ * For methods with return type {@code void}, the last parameter of the method is regarded as {@code MappingTarget},
+ * unless another parameter carries this annotation.
+ *
+ * @author Andreas Gudian
+ */
+@Target( ElementType.PARAMETER )
+public @interface MappingTarget {
+}
diff --git a/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java b/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java
index 0ad4cec92..2f50b97c7 100644
--- a/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java
+++ b/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java
@@ -43,6 +43,7 @@ import org.mapstruct.IterableMapping;
import org.mapstruct.MapMapping;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
+import org.mapstruct.MappingTarget;
import org.mapstruct.Mappings;
import org.mapstruct.ap.model.Options;
import org.mapstruct.ap.model.ReportingPolicy;
@@ -76,7 +77,8 @@ import org.mapstruct.ap.processor.ModelElementProcessor.ProcessorContext;
@GeneratePrism(value = Mapping.class, publicAccess = true),
@GeneratePrism(value = Mappings.class, publicAccess = true),
@GeneratePrism(value = IterableMapping.class, publicAccess = true),
- @GeneratePrism(value = MapMapping.class, publicAccess = true)
+ @GeneratePrism(value = MapMapping.class, publicAccess = true),
+ @GeneratePrism(value = MappingTarget.class, publicAccess = true)
})
@SupportedOptions({
MappingProcessor.SUPPRESS_GENERATOR_TIMESTAMP,
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 1cd7134c9..3ac1cdf77 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/BeanMappingMethod.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/BeanMappingMethod.java
@@ -21,6 +21,8 @@ package org.mapstruct.ap.model;
import java.util.List;
import java.util.Set;
+import org.mapstruct.ap.model.source.Parameter;
+
/**
* A {@link MappingMethod} implemented by a {@link Mapper} class which maps one
* bean type to another, optionally configured by one or more
@@ -32,9 +34,10 @@ public class BeanMappingMethod extends MappingMethod {
private final List propertyMappings;
- public BeanMappingMethod(String name, String parameterName, Type sourceType, Type targetType,
+ public BeanMappingMethod(String name, List parameters, List sourceParameters,
+ Type resultType, String resultName, Type returnType,
List propertyMappings) {
- super( name, parameterName, sourceType, targetType );
+ super( name, parameters, sourceParameters, resultType, resultName, returnType );
this.propertyMappings = propertyMappings;
}
diff --git a/processor/src/main/java/org/mapstruct/ap/model/IterableMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/model/IterableMappingMethod.java
index 1d43483d9..a14ad5aa1 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/IterableMappingMethod.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/IterableMappingMethod.java
@@ -18,8 +18,11 @@
*/
package org.mapstruct.ap.model;
+import java.util.List;
import java.util.Set;
+import org.mapstruct.ap.model.source.Parameter;
+
/**
* A {@link MappingMethod} implemented by a {@link Mapper} class which maps one iterable type to another. The collection
* elements are mapped either by a {@link TypeConversion} or another mapping method.
@@ -31,9 +34,10 @@ public class IterableMappingMethod extends MappingMethod {
private final MappingMethodReference elementMappingMethod;
private final TypeConversion conversion;
- public IterableMappingMethod(String name, String parameterName, Type sourceType, Type targetType,
+ public IterableMappingMethod(String name, List parameters, List sourceParameters,
+ Type resultType, String resultName, Type returnType,
MappingMethodReference elementMappingMethod, TypeConversion conversion) {
- super( name, parameterName, sourceType, targetType );
+ super( name, parameters, sourceParameters, resultType, resultName, returnType );
this.elementMappingMethod = elementMappingMethod;
this.conversion = conversion;
}
diff --git a/processor/src/main/java/org/mapstruct/ap/model/MapMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/model/MapMappingMethod.java
index ea06a614f..e127765bb 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/MapMappingMethod.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/MapMappingMethod.java
@@ -18,8 +18,11 @@
*/
package org.mapstruct.ap.model;
+import java.util.List;
import java.util.Set;
+import org.mapstruct.ap.model.source.Parameter;
+
/**
* A {@link MappingMethod} implemented by a {@link Mapper} class which maps one {@code Map} type to another. Keys and
* values are mapped either by a {@link TypeConversion} or another mapping method if required.
@@ -33,10 +36,11 @@ public class MapMappingMethod extends MappingMethod {
private final MappingMethodReference valueMappingMethod;
private final TypeConversion valueConversion;
- public MapMappingMethod(String name, String parameterName, Type sourceType, Type targetType,
+ public MapMappingMethod(String name, List parameters, List sourceParameters, Type resultType,
+ String resultName, Type returnType,
MappingMethodReference keyMappingMethod, TypeConversion keyConversion,
MappingMethodReference valueMappingMethod, TypeConversion valueConversion) {
- super( name, parameterName, sourceType, targetType );
+ super( name, parameters, sourceParameters, resultType, resultName, returnType );
this.keyMappingMethod = keyMappingMethod;
this.keyConversion = keyConversion;
@@ -68,8 +72,4 @@ public class MapMappingMethod extends MappingMethod {
return types;
}
-
- public String getReturnValueName() {
- return "map";
- }
}
diff --git a/processor/src/main/java/org/mapstruct/ap/model/MappingMethod.java b/processor/src/main/java/org/mapstruct/ap/model/MappingMethod.java
index 5a57d02d3..2aeb14ace 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/MappingMethod.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/MappingMethod.java
@@ -19,8 +19,11 @@
package org.mapstruct.ap.model;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
+import org.mapstruct.ap.model.source.Parameter;
+
/**
* A method implemented or referenced by a {@link Mapper} class.
*
@@ -29,38 +32,62 @@ import java.util.Set;
public abstract class MappingMethod extends AbstractModelElement {
private final String name;
- private final String parameterName;
- private final Type sourceType;
- private final Type targetType;
+ private final List parameters;
+ private final List sourceParameters;
+ private final Type resultType;
+ private final String resultName;
+ private final Type returnType;
+ private final boolean existingInstanceMapping;
- protected MappingMethod(String name, String parameterName, Type sourceType, Type targetType) {
+ protected MappingMethod(String name, List parameters, List sourceParameters, Type resultType,
+ String resultName, Type returnType) {
this.name = name;
- this.parameterName = parameterName;
- this.sourceType = sourceType;
- this.targetType = targetType;
+ this.parameters = parameters;
+ this.sourceParameters = sourceParameters;
+ this.resultType = resultType;
+ this.resultName = resultName;
+ this.returnType = returnType;
+ this.existingInstanceMapping =
+ ( null != parameters && null != sourceParameters && parameters.size() > sourceParameters.size() );
}
public String getName() {
return name;
}
- public String getParameterName() {
- return parameterName;
+ public List getParameters() {
+ return parameters;
}
- public Type getSourceType() {
- return sourceType;
+ public List getSourceParameters() {
+ return sourceParameters;
}
- public Type getTargetType() {
- return targetType;
+ public Type getResultType() {
+ return resultType;
+ }
+
+ public String getResultName() {
+ return resultName;
+ }
+
+ public Type getReturnType() {
+ return returnType;
+ }
+
+ public boolean isExistingInstanceMapping() {
+ return existingInstanceMapping;
}
@Override
public Set getImportTypes() {
Set types = new HashSet();
- types.add( getSourceType() );
- types.add( getTargetType() );
+
+ for ( Parameter param : getParameters() ) {
+ types.add( param.getType() );
+ }
+
+ types.add( getReturnType() );
return types;
}
@@ -69,7 +96,7 @@ public abstract class MappingMethod extends AbstractModelElement {
public String toString() {
return "MappingMethod {" +
"\n name='" + name + "\'," +
- "\n parameterName='" + parameterName + "\'," +
+ "\n parameters='" + parameters + "\'," +
"\n}";
}
}
diff --git a/processor/src/main/java/org/mapstruct/ap/model/MappingMethodReference.java b/processor/src/main/java/org/mapstruct/ap/model/MappingMethodReference.java
index 311546cf2..874b4462e 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/MappingMethodReference.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/MappingMethodReference.java
@@ -30,9 +30,8 @@ public class MappingMethodReference extends MappingMethod {
private final Type declaringMapper;
- public MappingMethodReference(Type declaringMapper, String name, String parameterName, Type sourceType,
- Type targetType) {
- super( name, parameterName, sourceType, targetType );
+ public MappingMethodReference(Type declaringMapper, String name) {
+ super( name, null, null, null, null, null );
this.declaringMapper = declaringMapper;
}
@@ -41,10 +40,6 @@ public class MappingMethodReference extends MappingMethod {
}
public Set getReferencedTypes() {
- Set types = new HashSet();
- types.add( getSourceType() );
- types.add( getTargetType() );
-
- return types;
+ return new HashSet();
}
}
diff --git a/processor/src/main/java/org/mapstruct/ap/model/Options.java b/processor/src/main/java/org/mapstruct/ap/model/Options.java
index 6af2b2084..2101960b3 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/Options.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/Options.java
@@ -22,7 +22,7 @@ package org.mapstruct.ap.model;
* The options passed to the code generator.
*
* @author Andreas Gudian
- * @autor Gunnar Morling
+ * @author Gunnar Morling
*/
public class Options {
private final boolean suppressGeneratorTimestamp;
diff --git a/processor/src/main/java/org/mapstruct/ap/model/Type.java b/processor/src/main/java/org/mapstruct/ap/model/Type.java
index dd8f8ea23..b0c2ccb13 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/Type.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/Type.java
@@ -38,6 +38,10 @@ import org.mapstruct.ap.util.Strings;
* @author Gunnar Morling
*/
public class Type extends AbstractModelElement implements Comparable {
+ /**
+ * Type representing {@code void}
+ */
+ public static final Type VOID = new Type( "void" );
private static final Set PRIMITIVE_TYPE_NAMES = new HashSet(
Arrays.asList( "boolean", "char", "byte", "short", "int", "long", "float", "double" )
@@ -61,6 +65,7 @@ public class Type extends AbstractModelElement implements Comparable {
DEFAULT_MAP_IMPLEMENTATION_TYPES.put( Map.class.getName(), forClass( HashMap.class ) );
}
+ private final String canonicalName;
private final String packageName;
private final String name;
private final List typeParameters;
@@ -77,6 +82,7 @@ public class Type extends AbstractModelElement implements Comparable {
if ( pakkage != null ) {
return new Type(
+ clazz.getCanonicalName(),
pakkage.getName(),
clazz.getSimpleName(),
clazz.isEnum(),
@@ -92,15 +98,16 @@ public class Type extends AbstractModelElement implements Comparable {
}
public Type(String name) {
- this( null, name, false, false, false, false, Collections.emptyList() );
+ this( name, null, name, false, false, false, false, Collections.emptyList() );
}
public Type(String packageName, String name) {
- this( packageName, name, false, false, false, false, Collections.emptyList() );
+ this( packageName + "." + name, packageName, name, false, false, false, false, Collections.emptyList() );
}
- public Type(String packageName, String name, boolean isEnumType, boolean isCollectionType,
+ public Type(String canonicalName, String packageName, String name, boolean isEnumType, boolean isCollectionType,
boolean isIterableType, boolean isMapType, List typeParameters) {
+ this.canonicalName = canonicalName;
this.packageName = packageName;
this.name = name;
this.isEnumType = isEnumType;
@@ -126,6 +133,7 @@ public class Type extends AbstractModelElement implements Comparable {
if ( isMapType ) {
Type mapType = DEFAULT_MAP_IMPLEMENTATION_TYPES.get( packageName + "." + name );
mapImplementationType = mapType != null ? new Type(
+ mapType.getPackageName() + "." + mapType.getName(),
mapType.getPackageName(),
mapType.getName(),
mapType.isEnumType(),
@@ -140,6 +148,10 @@ public class Type extends AbstractModelElement implements Comparable {
}
}
+ public String getCanonicalName() {
+ return canonicalName;
+ }
+
public String getPackageName() {
return packageName;
}
diff --git a/processor/src/main/java/org/mapstruct/ap/model/source/Method.java b/processor/src/main/java/org/mapstruct/ap/model/source/Method.java
index 3887b71d2..505a1793a 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/source/Method.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/source/Method.java
@@ -18,7 +18,10 @@
*/
package org.mapstruct.ap.model.source;
+import java.util.Arrays;
import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import javax.lang.model.element.ExecutableElement;
@@ -34,23 +37,28 @@ public class Method {
private final Type declaringMapper;
private final ExecutableElement executable;
- private final String parameterName;
- private final Type sourceType;
- private final Type targetType;
+ private final List parameters;
+ private final List sourceParameters;
+ private final String resultName;
+ private final Type resultType;
+ private final Type returnType;
private Map mappings;
private IterableMapping iterableMapping;
private MapMapping mapMapping;
- public static Method forMethodRequiringImplementation(ExecutableElement executable, String parameterName,
- Type sourceType, Type targetType,
+ public static Method forMethodRequiringImplementation(ExecutableElement executable, List parameters,
+ List sourceParameters, Type resultType,
+ String resultName, Type targetType,
Map mappings,
IterableMapping iterableMapping, MapMapping mapMapping) {
return new Method(
null,
executable,
- parameterName,
- sourceType,
+ parameters,
+ sourceParameters,
+ resultType,
+ resultName,
targetType,
mappings,
iterableMapping,
@@ -64,8 +72,10 @@ public class Method {
return new Method(
declaringMapper,
executable,
- parameterName,
- sourceType,
+ Arrays.asList( new Parameter( parameterName, sourceType ) ),
+ Arrays.asList( new Parameter( parameterName, sourceType ) ),
+ targetType,
+ null,
targetType,
Collections.emptyMap(),
null,
@@ -73,14 +83,18 @@ public class Method {
);
}
- private Method(Type declaringMapper, ExecutableElement executable, String parameterName, Type sourceType,
- Type targetType, Map mappings, IterableMapping iterableMapping,
+ private Method(Type declaringMapper, ExecutableElement executable, List parameters,
+ List sourceParameters, Type resultType, String resultName,
+ Type returnType,
+ Map mappings, IterableMapping iterableMapping,
MapMapping mapMapping) {
this.declaringMapper = declaringMapper;
this.executable = executable;
- this.parameterName = parameterName;
- this.sourceType = sourceType;
- this.targetType = targetType;
+ this.parameters = parameters;
+ this.sourceParameters = sourceParameters;
+ this.resultType = resultType;
+ this.resultName = resultName;
+ this.returnType = returnType;
this.mappings = mappings;
this.iterableMapping = iterableMapping;
this.mapMapping = mapMapping;
@@ -105,16 +119,24 @@ public class Method {
return executable.getSimpleName().toString();
}
- public String getParameterName() {
- return parameterName;
+ public List getParameters() {
+ return parameters;
}
- public Type getSourceType() {
- return sourceType;
+ public String getResultName() {
+ return resultName;
}
- public Type getTargetType() {
- return targetType;
+ public List getSourceParameters() {
+ return sourceParameters;
+ }
+
+ public Type getResultType() {
+ return resultType;
+ }
+
+ public Type getReturnType() {
+ return returnType;
}
public Map getMappings() {
@@ -143,16 +165,20 @@ public class Method {
public boolean reverses(Method method) {
return
- equals( sourceType, method.getTargetType() ) &&
- equals( targetType, method.getSourceType() );
+ equals( getSingleSourceType(), method.getReturnType() )
+ && equals( returnType, method.getSingleSourceType() );
+ }
+
+ public Type getSingleSourceType() {
+ return sourceParameters.size() == 1 ? sourceParameters.get( 0 ).getType() : null;
}
public boolean isIterableMapping() {
- return sourceType.isIterableType() && targetType.isIterableType();
+ return getSingleSourceType().isIterableType() && resultType.isIterableType();
}
public boolean isMapMapping() {
- return sourceType.isMapType() && targetType.isMapType();
+ return getSingleSourceType().isMapType() && resultType.isMapType();
}
private boolean equals(Object o1, Object o2) {
@@ -161,6 +187,19 @@ public class Method {
@Override
public String toString() {
- return targetType + " " + getName() + "(" + sourceType + " " + parameterName + ")";
+ return returnType + " " + getName() + "(" + getParamsList() + ")";
+ }
+
+ private String getParamsList() {
+ StringBuilder sb = new StringBuilder();
+ for ( Iterator it = parameters.iterator(); it.hasNext(); ) {
+ Parameter param = it.next();
+ sb.append( param.getType() ).append( " " ).append( param.getName() );
+ if ( it.hasNext() ) {
+ sb.append( ", " );
+ }
+ }
+
+ return sb.toString();
}
}
diff --git a/processor/src/main/java/org/mapstruct/ap/model/source/Parameter.java b/processor/src/main/java/org/mapstruct/ap/model/source/Parameter.java
index 7406b0ac4..34db914e4 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/source/Parameter.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/source/Parameter.java
@@ -29,10 +29,16 @@ public class Parameter {
private final String name;
private final Type type;
+ private final boolean mappingTarget;
- public Parameter(String name, Type type) {
+ public Parameter(String name, Type type, boolean mappingTarget) {
this.name = name;
this.type = type;
+ this.mappingTarget = mappingTarget;
+ }
+
+ public Parameter(String name, Type type) {
+ this( name, type, false );
}
public String getName() {
@@ -43,8 +49,12 @@ public class Parameter {
return type;
}
+ public boolean isMappingTarget() {
+ return mappingTarget;
+ }
+
@Override
public String toString() {
- return type.toString() + " " + name;
+ return ( mappingTarget ? "@MappingTarget " : "" ) + type.toString() + " " + name;
}
}
diff --git a/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java
index 093df31a8..47643f996 100644
--- a/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java
+++ b/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java
@@ -54,6 +54,7 @@ import org.mapstruct.ap.model.Type;
import org.mapstruct.ap.model.TypeConversion;
import org.mapstruct.ap.model.source.Mapping;
import org.mapstruct.ap.model.source.Method;
+import org.mapstruct.ap.model.source.Parameter;
import org.mapstruct.ap.util.Executables;
import org.mapstruct.ap.util.Filters;
import org.mapstruct.ap.util.Strings;
@@ -200,19 +201,14 @@ public class MapperCreationProcessor implements ModelElementProcessor mappings = method.getMappings();
- TypeElement returnTypeElement = (TypeElement) typeUtils.asElement( method.getExecutable().getReturnType() );
- TypeElement parameterElement = (TypeElement) typeUtils.asElement(
- method.getExecutable()
- .getParameters()
- .get( 0 )
- .asType()
- );
+ TypeElement resultTypeElement = elementUtils.getTypeElement( method.getResultType().getCanonicalName() );
+ TypeElement parameterElement = elementUtils.getTypeElement( method.getSingleSourceType().getCanonicalName() );
List sourceGetters = Filters.getterMethodsIn(
elementUtils.getAllMembers( parameterElement )
);
List targetSetters = Filters.setterMethodsIn(
- elementUtils.getAllMembers( returnTypeElement )
+ elementUtils.getAllMembers( resultTypeElement )
);
Set sourceProperties = executables.getPropertyNames(
@@ -257,9 +253,11 @@ public class MapperCreationProcessor implements ModelElementProcessor methods, Method method, ExecutableElement getterMethod,
ExecutableElement setterMethod, String dateFormat) {
Type sourceType = executables.retrieveReturnType( getterMethod );
- Type targetType = executables.retrieveParameter( setterMethod ).getType();
+ Type targetType = executables.retrieveSingleParameter( setterMethod ).getType();
MappingMethodReference propertyMappingMethod = getMappingMethodReference( methods, sourceType, targetType );
TypeConversion conversion = getConversion(
sourceType,
targetType,
dateFormat,
- method.getParameterName() + "." + getterMethod.getSimpleName().toString() + "()"
+ method.getSourceParameters().get( 0 ).getName() + "."
+ + getterMethod.getSimpleName().toString() + "()"
);
PropertyMapping property = new PropertyMapping(
- method.getParameterName(),
- Introspector.decapitalize( method.getTargetType().getName() ),
+ method.getSourceParameters().get( 0 ).getName(),
+ method.getResultName(),
executables.getPropertyName( getterMethod ),
getterMethod.getSimpleName().toString(),
sourceType,
@@ -360,8 +359,8 @@ public class MapperCreationProcessor implements ModelElementProcessor methods, Method method) {
- Type sourceElementType = method.getSourceType().getTypeParameters().get( 0 );
- Type targetElementType = method.getTargetType().getTypeParameters().get( 0 );
+ Type sourceElementType = method.getSourceParameters().get( 0 ).getType().getTypeParameters().get( 0 );
+ Type targetElementType = method.getResultType().getTypeParameters().get( 0 );
TypeConversion conversion = getConversion(
sourceElementType,
@@ -372,19 +371,24 @@ public class MapperCreationProcessor implements ModelElementProcessor methods, Method method) {
- Type sourceKeyType = method.getSourceType().getTypeParameters().get( 0 );
- Type sourceValueType = method.getSourceType().getTypeParameters().get( 1 );
- Type targetKeyType = method.getTargetType().getTypeParameters().get( 0 );
- Type targetValueType = method.getTargetType().getTypeParameters().get( 1 );
+ List sourceTypeParams = method.getSourceParameters().get( 0 ).getType().getTypeParameters();
+ Type sourceKeyType = sourceTypeParams.get( 0 );
+ Type sourceValueType = sourceTypeParams.get( 1 );
+
+ List resultTypeParams = method.getResultType().getTypeParameters();
+ Type targetKeyType = resultTypeParams.get( 0 );
+ Type targetValueType = resultTypeParams.get( 1 );
String keyDateFormat = method.getMapMapping() != null ? method.getMapMapping().getKeyFormat() : null;
String valueDateFormat = method.getMapMapping() != null ? method.getMapMapping().getValueFormat() : null;
@@ -406,9 +410,11 @@ public class MapperCreationProcessor implements ModelElementProcessor methods, Type parameterType,
Type returnType) {
for ( Method oneMethod : methods ) {
- if ( oneMethod.getSourceType().equals( parameterType ) && oneMethod.getTargetType().equals( returnType ) ) {
+ Parameter singleSourceParam = oneMethod.getSourceParameters().get( 0 );
+
+ if ( singleSourceParam.getType().equals( parameterType ) &&
+ oneMethod.getReturnType().equals( returnType ) ) {
return new MappingMethodReference(
oneMethod.getDeclaringMapper(),
- oneMethod.getName(),
- oneMethod.getParameterName(),
- oneMethod.getSourceType(),
- oneMethod.getTargetType()
+ oneMethod.getName()
);
}
}
diff --git a/processor/src/main/java/org/mapstruct/ap/processor/MethodRetrievalProcessor.java b/processor/src/main/java/org/mapstruct/ap/processor/MethodRetrievalProcessor.java
index 2d9f34615..0c3d496f2 100644
--- a/processor/src/main/java/org/mapstruct/ap/processor/MethodRetrievalProcessor.java
+++ b/processor/src/main/java/org/mapstruct/ap/processor/MethodRetrievalProcessor.java
@@ -18,6 +18,7 @@
*/
package org.mapstruct.ap.processor;
+import java.beans.Introspector;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -115,19 +116,26 @@ public class MethodRetrievalProcessor implements ModelElementProcessor parameters = executables.retrieveParameters( method );
Type returnType = executables.retrieveReturnType( method );
//add method with property mappings if an implementation needs to be generated
if ( implementationRequired ) {
- boolean isValid = checkParameterAndReturnType( method, parameter.getType(), returnType );
+ List sourceParameters = extractSourceParameters( parameters );
+ Parameter targetParameter = extractTargetParameter( parameters );
+ Type resultType = selectResultType( returnType, targetParameter );
+
+ boolean isValid =
+ checkParameterAndReturnType( method, sourceParameters, targetParameter, resultType, returnType );
if ( isValid ) {
return
Method.forMethodRequiringImplementation(
method,
- parameter.getName(),
- parameter.getType(),
+ parameters,
+ sourceParameters,
+ resultType,
+ selectResultName( targetParameter, resultType ),
returnType,
getMappings( method ),
IterableMapping.fromPrism( IterableMappingPrism.getInstanceOn( method ) ),
@@ -139,20 +147,102 @@ public class MethodRetrievalProcessor implements ModelElementProcessor parameters) {
+ for ( Parameter param : parameters ) {
+ if ( param.isMappingTarget() ) {
+ return param;
+ }
+ }
+
+ return null;
+ }
+
+ private List extractSourceParameters(List parameters) {
+ List sourceParameters = new ArrayList( parameters.size() );
+ for ( Parameter param : parameters ) {
+ if ( !param.isMappingTarget() ) {
+ sourceParameters.add( param );
+ }
+ }
+
+ return sourceParameters;
+ }
+
+ private String selectResultName(Parameter targetParameter, Type resultType) {
+ if ( null != targetParameter ) {
+ return targetParameter.getName();
+ }
+ else {
+ return Introspector.decapitalize( resultType.getName() );
+ }
+ }
+
+ private Type selectResultType(Type returnType, Parameter targetParameter) {
+ if ( null != targetParameter ) {
+ return targetParameter.getType();
+ }
+ else {
+ return returnType;
+ }
+ }
+
+ private boolean checkParameterAndReturnType(ExecutableElement method, List sourceParameters,
+ Parameter targetParameter, Type resultType, Type returnType) {
+ if ( sourceParameters.isEmpty() ) {
+ messager.printMessage( Kind.ERROR, "Can't generate mapping method with no input arguments.", method );
+ return false;
+ }
+
+ if ( sourceParameters.size() > 1 ) {
+ messager.printMessage(
+ Kind.ERROR,
+ "Mappings from more than one source objects are not yet supported.",
+ method
+ );
+ return false;
+ }
+
+ if ( targetParameter != null && ( sourceParameters.size() + 1 != method.getParameters().size() ) ) {
+ messager.printMessage(
+ Kind.ERROR,
+ "Can't generate mapping method with more than one @MappingTarget parameter.",
+ method
+ );
+ return false;
+ }
+
+ if ( resultType == Type.VOID ) {
+ messager.printMessage( Kind.ERROR, "Can't generate mapping method with return type void.", method );
+ return false;
+ }
+
+ if ( returnType != Type.VOID && !typeUtil.isAssignable( resultType, returnType ) ) {
+ messager.printMessage(
+ Kind.ERROR,
+ "The result type is not assignable to the the return type.",
+ method
+ );
+ return false;
+ }
+
+ Type parameterType = sourceParameters.get( 0 ).getType();
+
+ if ( parameterType.isIterableType() && !resultType.isIterableType() ) {
messager.printMessage(
Kind.ERROR,
"Can't generate mapping method from iterable type to non-iterable type.",
@@ -161,7 +251,7 @@ public class MethodRetrievalProcessor implements ModelElementProcessor parameters = method.getParameters();
if ( parameters.size() != 1 ) {
@@ -117,10 +120,42 @@ public class Executables {
return new Parameter(
parameter.getSimpleName().toString(),
- typeUtil.retrieveType( parameter.asType() )
+ typeUtil.retrieveType( parameter.asType() ),
+ false
);
}
+ public List retrieveParameters(ExecutableElement method) {
+ List extends VariableElement> parameters = method.getParameters();
+ List result = new ArrayList( parameters.size() );
+
+ boolean mappingTargetDefined = false;
+ for ( Iterator extends VariableElement> it = parameters.iterator(); it.hasNext(); ) {
+ VariableElement parameter = it.next();
+
+ boolean isExplicitMappingTarget = null != MappingTargetPrism.getInstanceOn( parameter );
+ mappingTargetDefined |= isExplicitMappingTarget;
+
+ result
+ .add(
+ new Parameter(
+ parameter.getSimpleName().toString(),
+ typeUtil.retrieveType( parameter.asType() ),
+ // the parameter is a mapping target, if it was either defined explicitly or if if this is the
+ // last parameter in a multi-argument void method
+ isExplicitMappingTarget
+ || ( !mappingTargetDefined && isMultiArgVoidMethod( method ) && !it.hasNext() )
+ )
+ );
+ }
+
+ return result;
+ }
+
+ public boolean isMultiArgVoidMethod(ExecutableElement method) {
+ return method.getParameters().size() > 1 && Type.VOID == retrieveReturnType( method );
+ }
+
public Type retrieveReturnType(ExecutableElement method) {
return typeUtil.retrieveType( method.getReturnType() );
}
diff --git a/processor/src/main/java/org/mapstruct/ap/util/TypeUtil.java b/processor/src/main/java/org/mapstruct/ap/util/TypeUtil.java
index 7c67c75e6..3d3b072d7 100644
--- a/processor/src/main/java/org/mapstruct/ap/util/TypeUtil.java
+++ b/processor/src/main/java/org/mapstruct/ap/util/TypeUtil.java
@@ -23,6 +23,7 @@ import java.util.Collection;
import java.util.List;
import java.util.Map;
import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
@@ -56,6 +57,7 @@ public class TypeUtil {
}
return new Type(
+ ( (TypeElement) type.asElement() ).getQualifiedName().toString(),
elementUtils.getPackageOf( type.asElement() ).toString(),
type.asElement().getSimpleName().toString(),
type.asElement().getKind() == ElementKind.ENUM,
@@ -85,8 +87,42 @@ public class TypeUtil {
else if ( mirror.getKind() == TypeKind.DECLARED ) {
return getType( ( (DeclaredType) mirror ) );
}
+ else if ( mirror.getKind() == TypeKind.VOID ) {
+ return Type.VOID;
+ }
else {
return new Type( mirror.toString() );
}
}
+
+ /**
+ * @param type1 first type
+ * @param type2 second type
+ *
+ * @return {@code true} if and only if the first type is assignable to the second
+ */
+ public boolean isAssignable(Type type1, Type type2) {
+ if ( type1.equals( type2 ) ) {
+ return true;
+ }
+
+ TypeMirror mirror1 = toTypeMirror( type1 );
+ TypeMirror mirror2 = toTypeMirror( type2 );
+ return null != mirror1 && null != mirror2 && typeUtils.isAssignable( mirror1, mirror2 );
+ }
+
+ private TypeMirror toTypeMirror(Type type) {
+ TypeElement rawType = elementUtils.getTypeElement( type.getCanonicalName() );
+
+ if ( null == rawType ) {
+ return null;
+ }
+
+ TypeMirror[] parameters = new TypeMirror[type.getTypeParameters().size()];
+ for ( int i = 0; i < type.getTypeParameters().size(); i++ ) {
+ parameters[i] = toTypeMirror( type.getTypeParameters().get( i ) );
+ }
+
+ return typeUtils.getDeclaredType( rawType, parameters );
+ }
}
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 11acaeb51..b88298d17 100644
--- a/processor/src/main/resources/org.mapstruct.ap.model.BeanMappingMethod.ftl
+++ b/processor/src/main/resources/org.mapstruct.ap.model.BeanMappingMethod.ftl
@@ -19,16 +19,19 @@
-->
@Override
- public ${targetType.name} ${name}(${sourceType.name} ${parameterName}) {
- if ( ${parameterName} == null ) {
- return null;
+ public ${returnType.name} ${name}(<#list parameters as param>${param.type.name} ${param.name}<#if param_has_next>, #if>#list>) {
+ if ( ${sourceParameters[0].name} == null ) {
+ return<#if returnType.name != "void"> null#if>;
}
-
- ${targetType.name} ${targetType.name?uncap_first} = new ${targetType.name}();
+ <#if !existingInstanceMapping>
+ ${resultType.name} ${resultName} = new ${resultType.name}();
+ #if>
<#list propertyMappings as propertyMapping>
<@includeModel object=propertyMapping/>
#list>
+ <#if returnType.name != "void">
- return ${targetType.name?uncap_first};
+ return ${resultName};
+ #if>
}
diff --git a/processor/src/main/resources/org.mapstruct.ap.model.IterableMappingMethod.ftl b/processor/src/main/resources/org.mapstruct.ap.model.IterableMappingMethod.ftl
index 0916185e1..7407abae0 100644
--- a/processor/src/main/resources/org.mapstruct.ap.model.IterableMappingMethod.ftl
+++ b/processor/src/main/resources/org.mapstruct.ap.model.IterableMappingMethod.ftl
@@ -19,23 +19,27 @@
-->
@Override
- public <@includeModel object=targetType/> ${name}(<@includeModel object=sourceType/> ${parameterName}) {
- if ( ${parameterName} == null ) {
- return null;
+ public <@includeModel object=returnType/> ${name}(<#list parameters as param><@includeModel object=param.type/> ${param.name}<#if param_has_next>, #if>#list>) {
+ if ( ${sourceParameters[0].name} == null ) {
+ return<#if returnType.name != "void"> null#if>;
}
+ <#if existingInstanceMapping>
+ ${resultName}.clear();
+ <#else>
<#-- Use the interface type on the left side, except it is java.lang.Iterable; use the implementation type - if present - on the right side -->
- <#if targetType.name == "Iterable" && targetType.packageName == "java.lang">${targetType.iterableImplementationType.name}<#else>${targetType.name}#if><<@includeModel object=targetType.typeParameters[0]/>> ${targetType.name?uncap_first} = new <#if targetType.iterableImplementationType??>${targetType.iterableImplementationType.name}<#else>${targetType.name}#if><<@includeModel object=targetType.typeParameters[0]/>>();
+ <#if resultType.name == "Iterable" && resultType.packageName == "java.lang">${resultType.iterableImplementationType.name}<#else>${resultType.name}#if><<@includeModel object=resultType.typeParameters[0]/>> ${resultName} = new <#if resultType.iterableImplementationType??>${resultType.iterableImplementationType.name}<#else>${resultType.name}#if><<@includeModel object=resultType.typeParameters[0]/>>();
+ #if>
- for ( <@includeModel object=sourceType.typeParameters[0]/> ${sourceType.typeParameters[0].name?uncap_first} : ${parameterName} ) {
+ for ( <@includeModel object=sourceParameters[0].type.typeParameters[0]/> ${sourceParameters[0].type.typeParameters[0].name?uncap_first} : ${sourceParameters[0].name} ) {
<#if elementMappingMethod??>
- ${targetType.name?uncap_first}.add( <@includeModel object=elementMappingMethod input="${sourceType.typeParameters[0].name?uncap_first}"/> );
+ ${resultName}.add( <@includeModel object=elementMappingMethod input="${sourceParameters[0].type.typeParameters[0].name?uncap_first}"/> );
<#else>
<#if (conversion.exceptionTypes?size == 0) >
- ${targetType.name?uncap_first}.add( <@includeModel object=conversion/> );
+ ${resultName}.add( <@includeModel object=conversion/> );
<#else>
try {
- ${targetType.name?uncap_first}.add( <@includeModel object=conversion/> );
+ ${resultName}.add( <@includeModel object=conversion/> );
}
<#list conversion.exceptionTypes as exceptionType>
catch( ${exceptionType.name} e ) {
@@ -45,6 +49,8 @@
#if>
#if>
}
+ <#if returnType.name != "void">
- return ${targetType.name?uncap_first};
+ return ${resultName};
+ #if>
}
diff --git a/processor/src/main/resources/org.mapstruct.ap.model.MapMappingMethod.ftl b/processor/src/main/resources/org.mapstruct.ap.model.MapMappingMethod.ftl
index 07ce9c77e..e20de89a6 100644
--- a/processor/src/main/resources/org.mapstruct.ap.model.MapMappingMethod.ftl
+++ b/processor/src/main/resources/org.mapstruct.ap.model.MapMappingMethod.ftl
@@ -19,23 +19,27 @@
-->
@Override
- public <@includeModel object=targetType /> ${name}(<@includeModel object=sourceType /> ${parameterName}) {
- if ( ${parameterName} == null ) {
- return null;
+ public <@includeModel object=returnType /> ${name}(<#list parameters as param><@includeModel object=param.type/> ${param.name}<#if param_has_next>, #if>#list>) {
+ if ( ${sourceParameters[0].name} == null ) {
+ return<#if returnType.name != "void"> null#if>;
}
- <@includeModel object=targetType /> ${returnValueName} = new <#if targetType.mapImplementationType??><@includeModel object=targetType.mapImplementationType /><#else><@includeModel object=targetType />#if>();
+ <#if existingInstanceMapping>
+ ${resultName}.clear();
+ <#else>
+ <@includeModel object=resultType /> ${resultName} = new <#if resultType.mapImplementationType??><@includeModel object=resultType.mapImplementationType /><#else><@includeModel object=resultType />#if>();
+ #if>
- for ( Map.Entry<<#list sourceType.typeParameters as typeParameter><@includeModel object=typeParameter /><#if typeParameter_has_next>, #if>#list>> entry : ${parameterName}.entrySet() ) {
+ for ( Map.Entry<<#list sourceParameters[0].type.typeParameters as typeParameter><@includeModel object=typeParameter /><#if typeParameter_has_next>, #if>#list>> entry : ${sourceParameters[0].name}.entrySet() ) {
<#-- key -->
<#if keyMappingMethod??>
- <@includeModel object=targetType.typeParameters[0]/> key = <@includeModel object=keyMappingMethod input="entry.getKey()"/>;
+ <@includeModel object=resultType.typeParameters[0]/> key = <@includeModel object=keyMappingMethod input="entry.getKey()"/>;
<#elseif keyConversion??>
<#if (keyConversion.exceptionTypes?size == 0) >
- <@includeModel object=targetType.typeParameters[0]/> key = <@includeModel object=keyConversion/>;
+ <@includeModel object=resultType.typeParameters[0]/> key = <@includeModel object=keyConversion/>;
<#else>
- <@includeModel object=targetType.typeParameters[0]/> key;
+ <@includeModel object=resultType.typeParameters[0]/> key;
try {
key = <@includeModel object=keyConversion/>;
}
@@ -46,16 +50,16 @@
#list>
#if>
<#else>
- <@includeModel object=targetType.typeParameters[0]/> key = entry.getKey();
+ <@includeModel object=resultType.typeParameters[0]/> key = entry.getKey();
#if>
<#-- value -->
<#if valueMappingMethod??>
- <@includeModel object=targetType.typeParameters[1]/> value = <@includeModel object=valueMappingMethod input="entry.getValue()"/>;
+ <@includeModel object=resultType.typeParameters[1]/> value = <@includeModel object=valueMappingMethod input="entry.getValue()"/>;
<#elseif valueConversion??>
<#if (valueConversion.exceptionTypes?size == 0) >
- <@includeModel object=targetType.typeParameters[1]/> value = <@includeModel object=valueConversion/>;
+ <@includeModel object=resultType.typeParameters[1]/> value = <@includeModel object=valueConversion/>;
<#else>
- <@includeModel object=targetType.typeParameters[1]/> value;
+ <@includeModel object=resultType.typeParameters[1]/> value;
try {
value = <@includeModel object=valueConversion/>;
}
@@ -66,11 +70,13 @@
#list>
#if>
<#else>
- <@includeModel object=targetType.typeParameters[1]/> value = entry.getValue();
+ <@includeModel object=resultType.typeParameters[1]/> value = entry.getValue();
#if>
- ${returnValueName}.put( key, value );
+ ${resultName}.put( key, value );
}
+ <#if returnType.name != "void">
- return ${returnValueName};
+ return ${resultName};
+ #if>
}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/defaultimplementation/DefaultCollectionImplementationTest.java b/processor/src/test/java/org/mapstruct/ap/test/collection/defaultimplementation/DefaultCollectionImplementationTest.java
index 45b20f1e8..b025484fc 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/collection/defaultimplementation/DefaultCollectionImplementationTest.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/collection/defaultimplementation/DefaultCollectionImplementationTest.java
@@ -18,8 +18,10 @@
*/
package org.mapstruct.ap.test.collection.defaultimplementation;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
+import java.util.List;
import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.MapperTestBase;
@@ -41,7 +43,7 @@ public class DefaultCollectionImplementationTest extends MapperTestBase {
@IssueKey("6")
public void shouldUseDefaultImplementationForList() {
Source source = new Source();
- source.setFooList( Arrays.asList( new SourceFoo( "Bob" ), new SourceFoo( "Alice" ) ) );
+ source.setFooList( createSourceFooList() );
Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source );
assertThat( target ).isNotNull();
@@ -52,7 +54,7 @@ public class DefaultCollectionImplementationTest extends MapperTestBase {
@IssueKey("6")
public void shouldUseDefaultImplementationForSet() {
Source source = new Source();
- source.setFooSet( new HashSet( Arrays.asList( new SourceFoo( "Bob" ), new SourceFoo( "Alice" ) ) ) );
+ source.setFooSet( new HashSet( createSourceFooList() ) );
Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source );
assertThat( target ).isNotNull();
@@ -63,7 +65,7 @@ public class DefaultCollectionImplementationTest extends MapperTestBase {
@IssueKey("6")
public void shouldUseDefaultImplementationForCollection() {
Source source = new Source();
- source.setFooCollection( Arrays.asList( new SourceFoo( "Bob" ), new SourceFoo( "Alice" ) ) );
+ source.setFooCollection( createSourceFooList() );
Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source );
assertThat( target ).isNotNull();
@@ -74,10 +76,49 @@ public class DefaultCollectionImplementationTest extends MapperTestBase {
@IssueKey("6")
public void shouldUseDefaultImplementationForIterable() {
Source source = new Source();
- source.setFooIterable( Arrays.asList( new SourceFoo( "Bob" ), new SourceFoo( "Alice" ) ) );
+ source.setFooIterable( createSourceFooList() );
Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source );
assertThat( target ).isNotNull();
- assertThat( target.getFooIterable() ).containsOnly( new TargetFoo( "Bob" ), new TargetFoo( "Alice" ) );
+ Iterable fooIterable = target.getFooIterable();
+ assertResultList( fooIterable );
+ }
+
+ private void assertResultList(Iterable fooIterable) {
+ assertThat( fooIterable ).isNotNull();
+ assertThat( fooIterable ).containsOnly( new TargetFoo( "Bob" ), new TargetFoo( "Alice" ) );
+ }
+
+ private List createSourceFooList() {
+ return Arrays.asList( new SourceFoo( "Bob" ), new SourceFoo( "Alice" ) );
+ }
+
+ @Test
+ @IssueKey("19")
+ public void existingMapping1() {
+ List target = new ArrayList();
+ SourceTargetMapper.INSTANCE.sourceFoosToTargetFoos1( createSourceFooList(), target );
+
+ assertResultList( target );
+ }
+
+ @Test
+ @IssueKey("19")
+ public void existingMapping2() {
+ List target = new ArrayList();
+ SourceTargetMapper.INSTANCE.sourceFoosToTargetFoos2( target, createSourceFooList() );
+
+ assertResultList( target );
+ }
+
+ @Test
+ @IssueKey("19")
+ public void existingMapping3() {
+ List target = new ArrayList();
+ Iterable result =
+ SourceTargetMapper.INSTANCE.sourceFoosToTargetFoos3( createSourceFooList(), target );
+
+ assertThat( target == result ).isTrue();
+ assertResultList( target );
}
}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/defaultimplementation/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/collection/defaultimplementation/SourceTargetMapper.java
index c0bb111a8..20cc46392 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/collection/defaultimplementation/SourceTargetMapper.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/collection/defaultimplementation/SourceTargetMapper.java
@@ -24,6 +24,7 @@ import java.util.Set;
import org.mapstruct.Mapper;
import org.mapstruct.Mappers;
+import org.mapstruct.MappingTarget;
@Mapper
public interface SourceTargetMapper {
@@ -41,4 +42,11 @@ public interface SourceTargetMapper {
Collection sourceFoosToTargetFoos(Collection foos);
Iterable sourceFoosToTargetFoos(Iterable foos);
+
+ void sourceFoosToTargetFoos1(Iterable sourceFoos, List targetFoos);
+
+ void sourceFoosToTargetFoos2(@MappingTarget List targetFoos, Iterable sourceFoos);
+
+ Iterable sourceFoosToTargetFoos3(Iterable sourceFoos,
+ @MappingTarget List targetFoos);
}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/map/MapMappingTest.java b/processor/src/test/java/org/mapstruct/ap/test/collection/map/MapMappingTest.java
index 412ff4093..c6f3c5dc4 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/collection/map/MapMappingTest.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/collection/map/MapMappingTest.java
@@ -58,12 +58,55 @@ public class MapMappingTest extends MapperTestBase {
@Test
public void shouldCreateReverseMapMethodImplementation() {
- Map values = new HashMap();
- values.put( "42", "01.01.1980" );
- values.put( "121", "20.07.2013" );
+ Map values = createStringStringMap();
Map target = SourceTargetMapper.INSTANCE.stringStringMapToLongDateMap( values );
+ assertResult( target );
+ }
+
+ @Test
+ @IssueKey("19")
+ public void shouldCreateReverseMapMethodImplementation1() {
+ Map values = createStringStringMap();
+
+ Map target = new HashMap();
+ target.put( 66L, new GregorianCalendar( 2013, 7, 16 ).getTime() );
+
+ SourceTargetMapper.INSTANCE.stringStringMapToLongDateMap( values, target );
+
+ assertResult( target );
+ }
+
+ @Test
+ @IssueKey("19")
+ public void shouldCreateReverseMapMethodImplementation2() {
+ Map values = createStringStringMap();
+
+ Map target = new HashMap();
+ target.put( 66L, new GregorianCalendar( 2013, 7, 16 ).getTime() );
+
+ SourceTargetMapper.INSTANCE.stringStringMapToLongDateMap2( target, values );
+
+ assertResult( target );
+ }
+
+ @Test
+ @IssueKey("19")
+ public void shouldCreateReverseMapMethodImplementation3() {
+ Map values = createStringStringMap();
+
+ Map target = new HashMap();
+ target.put( 66L, new GregorianCalendar( 2013, 7, 16 ).getTime() );
+
+ Map returnedTarget = SourceTargetMapper.INSTANCE.stringStringMapToLongDateMap3( values, target );
+
+ assertThat( target ).isSameAs( returnedTarget );
+
+ assertResult( target );
+ }
+
+ private void assertResult(Map target) {
assertThat( target ).isNotNull();
assertThat( target ).hasSize( 2 );
assertThat( target ).includes(
@@ -72,6 +115,13 @@ public class MapMappingTest extends MapperTestBase {
);
}
+ private Map createStringStringMap() {
+ Map values = new HashMap();
+ values.put( "42", "01.01.1980" );
+ values.put( "121", "20.07.2013" );
+ return values;
+ }
+
@Test
public void shouldInvokeMapMethodImplementationForMapTypedProperty() {
Map values = new HashMap();
@@ -94,9 +144,7 @@ public class MapMappingTest extends MapperTestBase {
@Test
public void shouldInvokeReverseMapMethodImplementationForMapTypedProperty() {
- Map values = new HashMap();
- values.put( "42", "01.01.1980" );
- values.put( "121", "20.07.2013" );
+ Map values = createStringStringMap();
Target target = new Target();
target.setValues( values );
diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/map/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/collection/map/SourceTargetMapper.java
index edb491793..4f1100306 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/collection/map/SourceTargetMapper.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/collection/map/SourceTargetMapper.java
@@ -24,17 +24,27 @@ import java.util.Map;
import org.mapstruct.MapMapping;
import org.mapstruct.Mapper;
import org.mapstruct.Mappers;
+import org.mapstruct.MappingTarget;
@Mapper(uses = CustomNumberMapper.class)
public interface SourceTargetMapper {
SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class );
- @MapMapping( valueDateFormat = "dd.MM.yyyy" )
+ @MapMapping(valueDateFormat = "dd.MM.yyyy")
Map longDateMapToStringStringMap(Map source);
Map stringStringMapToLongDateMap(Map source);
+ @MapMapping(valueDateFormat = "dd.MM.yyyy")
+ void stringStringMapToLongDateMap(Map source, Map target);
+
+ @MapMapping(valueDateFormat = "dd.MM.yyyy")
+ void stringStringMapToLongDateMap2(@MappingTarget Map target, Map source);
+
+ @MapMapping(valueDateFormat = "dd.MM.yyyy")
+ Map stringStringMapToLongDateMap3(Map source, @MappingTarget Map target);
+
Target sourceToTarget(Source source);
Source targetToSource(Target target);
diff --git a/processor/src/test/java/org/mapstruct/ap/test/inheritance/InheritanceTest.java b/processor/src/test/java/org/mapstruct/ap/test/inheritance/InheritanceTest.java
index 799868247..e898c7f5b 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/inheritance/InheritanceTest.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/inheritance/InheritanceTest.java
@@ -36,17 +36,61 @@ public class InheritanceTest extends MapperTestBase {
@Test
@IssueKey("17")
public void shouldMapAttributeFromSuperType() {
- SourceExt source = new SourceExt();
- source.setFoo( 42 );
- source.setBar( 23L );
+ SourceExt source = createSource();
TargetExt target = SourceTargetMapper.INSTANCE.sourceToTarget( source );
+ assertResult( target );
+ }
+
+ @Test
+ @IssueKey("19")
+ public void existingMapping1() {
+ SourceExt source = createSource();
+
+ TargetExt target = new TargetExt();
+ SourceTargetMapper.INSTANCE.sourceToTarget1( source, target );
+
+ assertResult( target );
+ }
+
+ @Test
+ @IssueKey("19")
+ public void existingMapping2() {
+ SourceExt source = createSource();
+
+ TargetExt target = new TargetExt();
+ SourceTargetMapper.INSTANCE.sourceToTarget2( target, source );
+
+ assertResult( target );
+ }
+
+ @Test
+ @IssueKey("19")
+ public void existingMapping3() {
+ SourceExt source = createSource();
+
+ TargetExt target = new TargetExt();
+ TargetBase result = SourceTargetMapper.INSTANCE.sourceToTarget3( source, target );
+
+ assertThat( target ).isSameAs( result );
+
+ assertResult( target );
+ }
+
+ private void assertResult(TargetExt target) {
assertThat( target ).isNotNull();
assertThat( target.getFoo() ).isEqualTo( Long.valueOf( 42 ) );
assertThat( target.getBar() ).isEqualTo( 23 );
}
+ private SourceExt createSource() {
+ SourceExt source = new SourceExt();
+ source.setFoo( 42 );
+ source.setBar( 23L );
+ return source;
+ }
+
@Test
@IssueKey("17")
public void shouldReverseMapAttributeFromSuperType() {
diff --git a/processor/src/test/java/org/mapstruct/ap/test/inheritance/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/inheritance/SourceTargetMapper.java
index 738f51d88..bfed071be 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/inheritance/SourceTargetMapper.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/inheritance/SourceTargetMapper.java
@@ -20,6 +20,7 @@ package org.mapstruct.ap.test.inheritance;
import org.mapstruct.Mapper;
import org.mapstruct.Mappers;
+import org.mapstruct.MappingTarget;
@Mapper
public interface SourceTargetMapper {
@@ -28,5 +29,11 @@ public interface SourceTargetMapper {
TargetExt sourceToTarget(SourceExt source);
+ void sourceToTarget1(SourceExt source, TargetExt target);
+
+ void sourceToTarget2(@MappingTarget TargetExt target, SourceExt source);
+
+ TargetBase sourceToTarget3(SourceExt source, @MappingTarget TargetExt target);
+
SourceExt targetToSource(TargetExt target);
}