diff --git a/core/src/main/java/org/mapstruct/MappingTarget.java b/core/src/main/java/org/mapstruct/MappingTarget.java
index 308488e0a..2a8c70e48 100644
--- a/core/src/main/java/org/mapstruct/MappingTarget.java
+++ b/core/src/main/java/org/mapstruct/MappingTarget.java
@@ -25,9 +25,6 @@ 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
*/
diff --git a/core/src/main/java/org/mapstruct/TargetType.java b/core/src/main/java/org/mapstruct/TargetType.java
new file mode 100644
index 000000000..34d44671e
--- /dev/null
+++ b/core/src/main/java/org/mapstruct/TargetType.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright 2012-2014 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 custom mapping method to be populated with the target type of the mapping.
+ *
+ * Not more than one parameter can be declared as {@code TargetType} and that parameter needs to be of type
+ * {@link Class} (may be parameterized), or a super-type of it.
+ *
+ * @author Andreas Gudian
+ */
+@Target( ElementType.PARAMETER )
+public @interface TargetType {
+}
diff --git a/processor/src/main/java/org/mapstruct/ap/model/MethodReference.java b/processor/src/main/java/org/mapstruct/ap/model/MethodReference.java
index c3809e381..cf359bc55 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/MethodReference.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/MethodReference.java
@@ -21,7 +21,9 @@ package org.mapstruct.ap.model;
import java.util.Set;
import org.mapstruct.ap.model.common.ConversionContext;
+import org.mapstruct.ap.model.common.Parameter;
import org.mapstruct.ap.model.common.Type;
+import org.mapstruct.ap.model.source.Method;
import org.mapstruct.ap.model.source.SourceMethod;
import org.mapstruct.ap.model.source.builtin.BuiltInMethod;
@@ -33,6 +35,7 @@ import org.mapstruct.ap.model.source.builtin.BuiltInMethod;
public class MethodReference extends MappingMethod {
private final MapperReference declaringMapper;
+ private final Type importType;
/**
* A reference to another mapping method in case this is a two-step mapping, e.g. from {@code JAXBElement} to
@@ -52,12 +55,21 @@ public class MethodReference extends MappingMethod {
super( method );
this.declaringMapper = declaringMapper;
this.contextParam = null;
+ this.importType = null;
}
public MethodReference(BuiltInMethod method, ConversionContext contextParam) {
super( method );
this.declaringMapper = null;
this.contextParam = method.getContextParameter( contextParam );
+ this.importType = null;
+ }
+
+ public MethodReference(Method method, MapperReference declaringMapper, Type importType) {
+ super( method );
+ this.declaringMapper = declaringMapper;
+ this.contextParam = null;
+ this.importType = importType;
}
public MapperReference getDeclaringMapper() {
@@ -72,6 +84,18 @@ public class MethodReference extends MappingMethod {
return contextParam;
}
+ /**
+ * @return the type of the single source parameter that is not the {@code @TargetType} parameter
+ */
+ public Type getSingleSourceParameterType() {
+ for ( Parameter parameter : getSourceParameters() ) {
+ if ( !parameter.isTargetType() ) {
+ return parameter.getType();
+ }
+ }
+ return null;
+ }
+
public void setMethodRefChild(MethodReference methodRefChild) {
this.methodRefChild = methodRefChild;
}
@@ -83,6 +107,9 @@ public class MethodReference extends MappingMethod {
@Override
public Set getImportTypes() {
Set imported = super.getImportTypes();
+ if ( importType != null ) {
+ imported.add( importType );
+ }
if ( methodRefChild != null ) {
imported.addAll( methodRefChild.getImportTypes() );
}
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 d54a72c92..8079d6449 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
@@ -32,15 +32,17 @@ public class Parameter extends ModelElement {
private final String name;
private final Type type;
private final boolean mappingTarget;
+ private final boolean targetType;
- public Parameter(String name, Type type, boolean mappingTarget) {
+ public Parameter(String name, Type type, boolean mappingTarget, boolean targetType) {
this.name = name;
this.type = type;
this.mappingTarget = mappingTarget;
+ this.targetType = targetType;
}
public Parameter(String name, Type type) {
- this( name, type, false );
+ this( name, type, false, false );
}
public String getName() {
@@ -57,11 +59,16 @@ public class Parameter extends ModelElement {
@Override
public String toString() {
- return ( mappingTarget ? "@MappingTarget " : "" ) + type.toString() + " " + name;
+ return ( mappingTarget ? "@MappingTarget " : "" ) + ( targetType ? "@TargetType " : "" )
+ + type.toString() + " " + name;
}
@Override
public Set getImportTypes() {
return Collections.asSet( type );
}
+
+ public boolean isTargetType() {
+ return targetType;
+ }
}
diff --git a/processor/src/main/java/org/mapstruct/ap/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/model/common/Type.java
index 298d69aba..c1eb9df79 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/common/Type.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/common/Type.java
@@ -22,6 +22,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
+
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
diff --git a/processor/src/main/java/org/mapstruct/ap/model/common/TypeFactory.java b/processor/src/main/java/org/mapstruct/ap/model/common/TypeFactory.java
index 19da1fb4f..ba00d7f77 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/common/TypeFactory.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/common/TypeFactory.java
@@ -41,6 +41,7 @@ import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.PrimitiveType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
@@ -48,6 +49,7 @@ import javax.lang.model.util.SimpleElementVisitor6;
import javax.lang.model.util.Types;
import org.mapstruct.ap.prism.MappingTargetPrism;
+import org.mapstruct.ap.prism.TargetTypePrism;
import org.mapstruct.ap.util.AnnotationProcessingException;
/**
@@ -185,6 +187,29 @@ public class TypeFactory {
);
}
+ /**
+ * Returns the Type that represents the declared Class type of the given type. For primitive types, the boxed class
+ * will be used.
+ * Examples:
+ * If type represents {@code java.lang.Integer}, it will return the type that represents {@code Class}.
+ *
+ * If type represents {@code int}, it will return the type that represents {@code Class}.
+ *
+ * @param type the type to return the declared class type for
+ * @return the type representing {@code Class}.
+ */
+ public Type classTypeOf(Type type) {
+ TypeMirror typeToUse;
+ if ( type.isPrimitive() ) {
+ typeToUse = typeUtils.boxedClass( (PrimitiveType) type.getTypeMirror() ).asType();
+ }
+ else {
+ typeToUse = type.getTypeMirror();
+ }
+
+ return getType( typeUtils.getDeclaredType( elementUtils.getTypeElement( "java.lang.Class" ), typeToUse ) );
+ }
+
public Parameter getSingleParameter(ExecutableElement method) {
List extends VariableElement> parameters = method.getParameters();
@@ -197,8 +222,7 @@ public class TypeFactory {
return new Parameter(
parameter.getSimpleName().toString(),
- getType( parameter.asType() ),
- false
+ getType( parameter.asType() )
);
}
@@ -212,7 +236,8 @@ public class TypeFactory {
new Parameter(
parameter.getSimpleName().toString(),
getType( parameter.asType() ),
- MappingTargetPrism.getInstanceOn( parameter ) != null
+ MappingTargetPrism.getInstanceOn( parameter ) != null,
+ TargetTypePrism.getInstanceOn( parameter ) != null
)
);
}
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 7ad65143a..3a2ad1fc3 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
@@ -36,15 +36,14 @@ import org.mapstruct.ap.model.source.builtin.BuiltInMethod;
public interface Method {
/**
- * Checks whether the provided sourceType and provided targetType match with the parameter respectively
- * return type of the method. The check also should incorporate wild card and generic type variables
+ * Checks whether the provided sourceType and provided targetType match with the parameter respectively return type
+ * of the method. The check also should incorporate wild card and generic type variables
*
- * @param sourceType the sourceType to match to the parameter
+ * @param sourceTypes the sourceTypes to match to the parameter
* @param targetType the targetType to match to the returnType
- *
* @return true when match
*/
- boolean matches(Type sourceType, Type targetType);
+ boolean matches(List sourceTypes, Type targetType);
/**
* Returns the mapper type declaring this method if it is not declared by the mapper interface currently processed
diff --git a/processor/src/main/java/org/mapstruct/ap/model/source/MethodMatcher.java b/processor/src/main/java/org/mapstruct/ap/model/source/MethodMatcher.java
index b488774ce..30f1e48eb 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/source/MethodMatcher.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/source/MethodMatcher.java
@@ -21,6 +21,7 @@ package org.mapstruct.ap.model.source;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.element.VariableElement;
@@ -61,10 +62,8 @@ import org.mapstruct.ap.model.common.Type;
*/
public class MethodMatcher {
-
private final SourceMethod candidateMethod;
private final Types typeUtils;
- private final Map genericTypesMap = new HashMap();
MethodMatcher(Types typeUtils, SourceMethod candidateMethod) {
this.typeUtils = typeUtils;
@@ -72,38 +71,56 @@ public class MethodMatcher {
}
/**
- * Whether the given source and target type are matched by this matcher's candidate method.
+ * Whether the given source and target types are matched by this matcher's candidate method.
*
- * @param sourceType the source type
+ * @param sourceTypes the source types
* @param targetType the target type
*
- * @return {@code true} when both, source type and target type match the signature of this matcher's method;
+ * @return {@code true} when both, source type and target types match the signature of this matcher's method;
* {@code false} otherwise.
*/
- boolean matches(Type sourceType, Type targetType) {
+ boolean matches(List sourceTypes, Type targetType) {
// check & collect generic types.
List extends VariableElement> candidateParameters = candidateMethod.getExecutable().getParameters();
- if ( candidateParameters.size() != 1 ) {
+ if ( candidateParameters.size() != sourceTypes.size() ) {
return false;
}
- TypeMatcher parameterMatcher = new TypeMatcher( Assignability.VISITED_ASSIGNABLE_FROM );
- if ( !parameterMatcher.visit( candidateParameters.iterator().next().asType(), sourceType.getTypeMirror() ) ) {
- return false;
+ Map genericTypesMap = new HashMap();
+
+ int i = 0;
+ for ( VariableElement candidateParameter : candidateParameters ) {
+ TypeMatcher parameterMatcher = new TypeMatcher( Assignability.VISITED_ASSIGNABLE_FROM, genericTypesMap );
+ if ( !parameterMatcher.visit( candidateParameter.asType(), sourceTypes.get( i++ ).getTypeMirror() ) ) {
+ return false;
+ }
}
// check return type
TypeMirror candidateReturnType = candidateMethod.getExecutable().getReturnType();
- TypeMatcher returnTypeMatcher = new TypeMatcher( Assignability.VISITED_ASSIGNABLE_TO );
+ TypeMatcher returnTypeMatcher = new TypeMatcher( Assignability.VISITED_ASSIGNABLE_TO, genericTypesMap );
+
if ( !returnTypeMatcher.visit( candidateReturnType, targetType.getTypeMirror() ) ) {
- return false;
+ if ( targetType.isPrimitive() ) {
+ TypeMirror boxedType = typeUtils.boxedClass( (PrimitiveType) targetType.getTypeMirror() ).asType();
+ TypeMatcher boxedReturnTypeMatcher =
+ new TypeMatcher( Assignability.VISITED_ASSIGNABLE_TO, genericTypesMap );
+
+ if ( !boxedReturnTypeMatcher.visit( candidateReturnType, boxedType ) ) {
+ return false;
+ }
+ }
+ else {
+ return false;
+ }
}
// check if all type parameters are indeed mapped
- if ( candidateMethod.getExecutable().getTypeParameters().size() != this.genericTypesMap.size() ) {
+ if ( candidateMethod.getExecutable().getTypeParameters().size() != genericTypesMap.size() ) {
return false;
}
+
// check if all entries are in the bounds
for ( Map.Entry entry : genericTypesMap.entrySet() ) {
if ( !isWithinBounds( entry.getValue(), getTypeParamFromCandidate( entry.getKey() ) ) ) {
@@ -120,10 +137,12 @@ public class MethodMatcher {
private class TypeMatcher extends SimpleTypeVisitor6 {
private final Assignability assignability;
+ private final Map genericTypesMap;
- public TypeMatcher(Assignability assignability) {
+ public TypeMatcher(Assignability assignability, Map genericTypesMap) {
super( Boolean.FALSE ); // default value
this.assignability = assignability;
+ this.genericTypesMap = genericTypesMap;
}
@Override
diff --git a/processor/src/main/java/org/mapstruct/ap/model/source/SourceMethod.java b/processor/src/main/java/org/mapstruct/ap/model/source/SourceMethod.java
index 245f6b9fd..a79117616 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/source/SourceMethod.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/source/SourceMethod.java
@@ -22,6 +22,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.util.Types;
@@ -58,11 +59,9 @@ public class SourceMethod implements Method {
private boolean configuredByReverseMappingMethod = false;
public static SourceMethod forMethodRequiringImplementation(ExecutableElement executable,
- List parameters,
- Type returnType, Map> mappings,
- IterableMapping iterableMapping,
- MapMapping mapMapping,
+ List parameters, Type returnType,
+ Map> mappings,
+ IterableMapping iterableMapping, MapMapping mapMapping,
Types typeUtils) {
return new SourceMethod(
@@ -73,51 +72,40 @@ public class SourceMethod implements Method {
mappings,
iterableMapping,
mapMapping,
- typeUtils
- );
+ typeUtils );
}
- public static SourceMethod forReferencedMethod(Type declaringMapper,
- ExecutableElement executable,
- List parameters,
- Type returnType,
- Types typeUtils) {
+ public static SourceMethod forReferencedMethod(Type declaringMapper, ExecutableElement executable,
+ List parameters, Type returnType, Types typeUtils) {
return new SourceMethod(
declaringMapper,
executable,
parameters,
returnType,
- Collections.>emptyMap(),
+ Collections.> emptyMap(),
null,
null,
- typeUtils
- );
+ typeUtils );
}
- public static SourceMethod forFactoryMethod(Type declaringMapper, ExecutableElement executable,
- Type returnType, Types typeUtils) {
+ public static SourceMethod forFactoryMethod(Type declaringMapper, ExecutableElement executable, Type returnType,
+ Types typeUtils) {
return new SourceMethod(
declaringMapper,
executable,
- Collections.emptyList(),
+ Collections. emptyList(),
returnType,
- Collections.>emptyMap(),
+ Collections.> emptyMap(),
null,
null,
- typeUtils
- );
+ typeUtils );
}
- private SourceMethod(Type declaringMapper,
- ExecutableElement executable,
- List parameters,
- Type returnType,
- Map> mappings,
- IterableMapping iterableMapping,
- MapMapping mapMapping,
- Types typeUtils) {
+ private SourceMethod(Type declaringMapper, ExecutableElement executable, List parameters,
+ Type returnType, Map> mappings, IterableMapping iterableMapping,
+ MapMapping mapMapping, Types typeUtils) {
this.declaringMapper = declaringMapper;
this.executable = executable;
this.parameters = parameters;
@@ -178,7 +166,7 @@ public class SourceMethod implements Method {
List sourceParameters = new ArrayList();
for ( Parameter parameter : parameters ) {
- if ( !parameter.isMappingTarget() ) {
+ if ( !parameter.isMappingTarget() && !parameter.isTargetType() ) {
sourceParameters.add( parameter );
}
}
@@ -336,9 +324,22 @@ public class SourceMethod implements Method {
* {@inheritDoc} {@link Method}
*/
@Override
- public boolean matches(Type sourceType, Type targetType) {
+ public boolean matches(List sourceTypes, Type targetType) {
MethodMatcher matcher = new MethodMatcher( typeUtils, this );
- return matcher.matches( sourceType, targetType );
+ return matcher.matches( sourceTypes, targetType );
}
+ /**
+ * @param parameters the parameter list to check
+ * @return true
, iff the parameter list contains a parameter annotated with {@code @TargetType}
+ */
+ public static boolean containsTargetTypeParameter(List parameters) {
+ for ( Parameter param : parameters ) {
+ if ( param.isTargetType() ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
}
diff --git a/processor/src/main/java/org/mapstruct/ap/model/source/builtin/BuiltInMethod.java b/processor/src/main/java/org/mapstruct/ap/model/source/builtin/BuiltInMethod.java
index 95588492a..e140bcf4b 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/source/builtin/BuiltInMethod.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/source/builtin/BuiltInMethod.java
@@ -68,7 +68,12 @@ public abstract class BuiltInMethod implements Method {
* excluding generic type variables. When the implementor sees a need for this, this method can be overridden.
*/
@Override
- public boolean matches(Type sourceType, Type targetType) {
+ public boolean matches(List sourceTypes, Type targetType) {
+ if ( sourceTypes.size() > 1 ) {
+ return false;
+ }
+ Type sourceType = sourceTypes.iterator().next();
+
if ( targetType.erasure().isAssignableTo( getReturnType().erasure() )
&& sourceType.erasure().isAssignableTo( getParameter().getType().erasure() ) ) {
return doTypeVarsMatch( sourceType, targetType );
diff --git a/processor/src/main/java/org/mapstruct/ap/model/source/selector/MethodSelectors.java b/processor/src/main/java/org/mapstruct/ap/model/source/selector/MethodSelectors.java
index 83dee2f9f..16904a78d 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/source/selector/MethodSelectors.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/source/selector/MethodSelectors.java
@@ -24,7 +24,9 @@ import java.util.List;
import javax.lang.model.util.Types;
+import org.mapstruct.ap.model.common.Parameter;
import org.mapstruct.ap.model.common.Type;
+import org.mapstruct.ap.model.common.TypeFactory;
import org.mapstruct.ap.model.source.Method;
import org.mapstruct.ap.model.source.SourceMethod;
@@ -37,10 +39,10 @@ public class MethodSelectors implements MethodSelector {
private final List selectors;
- public MethodSelectors(Types typeUtils) {
+ public MethodSelectors(Types typeUtils, TypeFactory typeFactory) {
selectors =
Arrays.asList(
- new TypeSelector(),
+ new TypeSelector( typeFactory ),
new InheritanceSelector(),
new XmlElementDeclSelector( typeUtils )
);
@@ -64,4 +66,26 @@ public class MethodSelectors implements MethodSelector {
}
return candidates;
}
+
+ /**
+ * @param typeFactory the type factory to use
+ * @param parameters the parameters to map the types for
+ * @param sourceType the source type
+ * @param returnType the return type
+ * @return the list of actual parameter types
+ */
+ public static List getParameterTypes(TypeFactory typeFactory, List parameters, Type sourceType,
+ Type returnType) {
+ List result = new ArrayList( parameters.size() );
+ for ( Parameter param : parameters ) {
+ if ( param.isTargetType() ) {
+ result.add( typeFactory.classTypeOf( returnType ) );
+ }
+ else {
+ result.add( sourceType );
+ }
+ }
+
+ return result;
+ }
}
diff --git a/processor/src/main/java/org/mapstruct/ap/model/source/selector/TypeSelector.java b/processor/src/main/java/org/mapstruct/ap/model/source/selector/TypeSelector.java
index d2d631149..7afc6572a 100644
--- a/processor/src/main/java/org/mapstruct/ap/model/source/selector/TypeSelector.java
+++ b/processor/src/main/java/org/mapstruct/ap/model/source/selector/TypeSelector.java
@@ -22,6 +22,7 @@ import java.util.ArrayList;
import java.util.List;
import org.mapstruct.ap.model.common.Type;
+import org.mapstruct.ap.model.common.TypeFactory;
import org.mapstruct.ap.model.source.Method;
import org.mapstruct.ap.model.source.MethodMatcher;
import org.mapstruct.ap.model.source.SourceMethod;
@@ -34,6 +35,12 @@ import org.mapstruct.ap.model.source.SourceMethod;
*/
public class TypeSelector implements MethodSelector {
+ private final TypeFactory typeFactory;
+
+ public TypeSelector(TypeFactory typeFactory) {
+ this.typeFactory = typeFactory;
+ }
+
@Override
public List getMatchingMethods(SourceMethod mappingMethod, List methods,
Type parameterType, Type returnType,
@@ -45,7 +52,9 @@ public class TypeSelector implements MethodSelector {
continue;
}
- if ( method.matches( parameterType, returnType ) ) {
+ List parameterTypes =
+ MethodSelectors.getParameterTypes( typeFactory, method.getParameters(), parameterType, returnType );
+ if ( method.matches( parameterTypes, returnType ) ) {
result.add( method );
}
}
diff --git a/processor/src/main/java/org/mapstruct/ap/prism/PrismGenerator.java b/processor/src/main/java/org/mapstruct/ap/prism/PrismGenerator.java
index 08d81108c..5e72094ee 100644
--- a/processor/src/main/java/org/mapstruct/ap/prism/PrismGenerator.java
+++ b/processor/src/main/java/org/mapstruct/ap/prism/PrismGenerator.java
@@ -27,6 +27,7 @@ import org.mapstruct.MapMapping;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
+import org.mapstruct.TargetType;
import org.mapstruct.Mappings;
/**
@@ -40,6 +41,7 @@ import org.mapstruct.Mappings;
@GeneratePrism(value = Mappings.class, publicAccess = true),
@GeneratePrism(value = IterableMapping.class, publicAccess = true),
@GeneratePrism(value = MapMapping.class, publicAccess = true),
+ @GeneratePrism(value = TargetType.class, publicAccess = true),
@GeneratePrism(value = MappingTarget.class, publicAccess = true),
// external types
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 db0c89b05..e858b29db 100644
--- a/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java
+++ b/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java
@@ -106,7 +106,7 @@ public class MapperCreationProcessor implements ModelElementProcessor();
- this.methodSelectors = new MethodSelectors( typeUtils );
+ this.methodSelectors = new MethodSelectors( typeUtils, typeFactory );
return getMapper( mapperTypeElement, sourceModel );
}
@@ -257,18 +257,21 @@ public class MapperCreationProcessor implements ModelElementProcessor paramterTypes =
+ MethodSelectors.getParameterTypes( typeFactory, method.getParameters(), null, returnType );
+
+ if ( method.matches( paramterTypes, returnType ) ) {
+ if ( result == null ) {
+ MapperReference mapperReference = findMapperReference( mapperReferences, method );
+
+ result =
+ new MethodReference(
+ method,
+ mapperReference,
+ SourceMethod.containsTargetTypeParameter( method.getParameters() )
+ ? returnType : null );
}
else {
messager.printMessage(
@@ -1012,7 +1015,7 @@ public class MapperCreationProcessor implements ModelElementProcessor T getBestMatch(SourceMethod mappingMethod,
String mappedElement,
List methods,
- Type parameterType,
+ Type sourceType,
Type returnType,
String targetPropertyName) {
List candidates = methodSelectors.getMatchingMethods(
mappingMethod,
methods,
- parameterType,
+ sourceType,
returnType,
targetPropertyName
);
@@ -1130,7 +1133,7 @@ public class MapperCreationProcessor implements ModelElementProcessor mapperReferences) {
- MapperReference mapperReference = null;
+ private MethodReference getMappingMethodReference(SourceMethod method, List mapperReferences,
+ Type targetType) {
+ MapperReference mapperReference = findMapperReference( mapperReferences, method );
+
+ return new MethodReference(
+ method,
+ mapperReference,
+ SourceMethod.containsTargetTypeParameter( method.getParameters() ) ? targetType : null );
+ }
+
+ private MapperReference findMapperReference(List mapperReferences, SourceMethod method) {
for ( MapperReference ref : mapperReferences ) {
if ( ref.getMapperType().equals( method.getDeclaringMapper() ) ) {
- mapperReference = ref;
- break;
+ return ref;
}
}
- return new MethodReference( method, mapperReference );
+ return null;
}
private MethodReference getMappingMethodReference(BuiltInMethod method, Type returnType, String dateFormat) {
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 e9e620c76..ac5a76b6b 100644
--- a/processor/src/main/java/org/mapstruct/ap/processor/MethodRetrievalProcessor.java
+++ b/processor/src/main/java/org/mapstruct/ap/processor/MethodRetrievalProcessor.java
@@ -18,10 +18,13 @@
*/
package org.mapstruct.ap.processor;
+import static javax.lang.model.util.ElementFilter.methodsIn;
+
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+
import javax.annotation.processing.Messager;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
@@ -46,8 +49,6 @@ import org.mapstruct.ap.prism.MappingPrism;
import org.mapstruct.ap.prism.MappingsPrism;
import org.mapstruct.ap.util.AnnotationProcessingException;
-import static javax.lang.model.util.ElementFilter.methodsIn;
-
/**
* A {@link ModelElementProcessor} which retrieves a list of {@link SourceMethod}s
* representing all the mapping methods of the given bean mapper type as well as
@@ -125,6 +126,7 @@ public class MethodRetrievalProcessor implements ModelElementProcessor sourceParameters = extractSourceParameters( parameters );
@@ -132,7 +134,13 @@ public class MethodRetrievalProcessor implements ModelElementProcessor parameters) {
+ return isValidReferencedOrFactoryMethod( 1, parameters );
+ }
+
+ private boolean isValidFactoryMethod(List parameters) {
+ return isValidReferencedOrFactoryMethod( 0, parameters );
+ }
+
+ private boolean isValidReferencedOrFactoryMethod(int sourceParamCount, List parameters) {
+ int validSourceParameters = 0;
+ int targetParameters = 0;
+ int targetTypeParameters = 0;
+
+ for ( Parameter param : parameters ) {
+ if ( param.isMappingTarget() ) {
+ targetParameters++;
+ }
+
+ if ( param.isTargetType() ) {
+ targetTypeParameters++;
+ }
+
+ if ( !param.isMappingTarget() && !param.isTargetType() ) {
+ validSourceParameters++;
+ }
+ }
+ return validSourceParameters == sourceParamCount && targetParameters == 0 && targetTypeParameters <= 1
+ && parameters.size() == validSourceParameters + targetParameters + targetTypeParameters;
+ }
+
private Parameter extractTargetParameter(List parameters) {
for ( Parameter param : parameters ) {
if ( param.isMappingTarget() ) {
@@ -207,7 +235,8 @@ public class MethodRetrievalProcessor implements ModelElementProcessor sourceParameters,
- Parameter targetParameter, Type resultType, Type returnType) {
+ Parameter targetParameter, Type resultType, Type returnType,
+ boolean containsTargetTypeParameter) {
if ( sourceParameters.isEmpty() ) {
messager.printMessage( Kind.ERROR, "Can't generate mapping method with no input arguments.", method );
return false;
@@ -248,6 +277,14 @@ public class MethodRetrievalProcessor implements ModelElementProcessor null#if>;
}
- <#if !existingInstanceMapping><@includeModel object=resultType/> ${resultName} = <#if factoryMethod??><@includeModel object=factoryMethod/><#else>new <@includeModel object=resultType/>()#if>;#if>
+ <#if !existingInstanceMapping><@includeModel object=resultType/> ${resultName} = <#if factoryMethod??><@includeModel object=factoryMethod targetType="${resultType.name}"/><#else>new <@includeModel object=resultType/>()#if>;#if>
<#if (sourceParameters?size > 1)>
<#list sourceParameters as sourceParam>
if ( ${sourceParam.name} != null ) {
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 43fbdac3e..055ebb664 100644
--- a/processor/src/main/resources/org.mapstruct.ap.model.IterableMappingMethod.ftl
+++ b/processor/src/main/resources/org.mapstruct.ap.model.IterableMappingMethod.ftl
@@ -33,7 +33,7 @@
for ( <@includeModel object=sourceParameter.type.typeParameters[0]/> ${loopVariableName} : ${sourceParameter.name} ) {
<#if elementMappingMethod??>
- ${resultName}.add( <@includeModel object=elementMappingMethod input="${loopVariableName}"/> );
+ ${resultName}.add( <@includeModel object=elementMappingMethod input="${loopVariableName}" targetType="${resultType.typeParameters[0].name}"/> );
<#elseif conversion??>
<#if (conversion.exceptionTypes?size == 0) >
${resultName}.add( <@includeModel object=conversion/> );
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 98f169976..31fd1006a 100644
--- a/processor/src/main/resources/org.mapstruct.ap.model.MapMappingMethod.ftl
+++ b/processor/src/main/resources/org.mapstruct.ap.model.MapMappingMethod.ftl
@@ -35,7 +35,7 @@
<#-- key -->
<#if keyMappingMethod??>
- <@includeModel object=resultType.typeParameters[0]/> ${keyVariableName} = <@includeModel object=keyMappingMethod input="entry.getKey()"/>;
+ <@includeModel object=resultType.typeParameters[0]/> ${keyVariableName} = <@includeModel object=keyMappingMethod input="entry.getKey()" targetType="${resultType.typeParameters[0].name}"/>;
<#elseif keyConversion??>
<#if (keyConversion.exceptionTypes?size == 0) >
<@includeModel object=resultType.typeParameters[0]/> ${keyVariableName} = <@includeModel object=keyConversion/>;
@@ -55,7 +55,7 @@
#if>
<#-- value -->
<#if valueMappingMethod??>
- <@includeModel object=resultType.typeParameters[1]/> ${valueVariableName} = <@includeModel object=valueMappingMethod input="entry.getValue()"/>;
+ <@includeModel object=resultType.typeParameters[1]/> ${valueVariableName} = <@includeModel object=valueMappingMethod input="entry.getValue()" targetType="${resultType.typeParameters[1].name}"/>;
<#elseif valueConversion??>
<#if (valueConversion.exceptionTypes?size == 0) >
<@includeModel object=resultType.typeParameters[1]/> ${valueVariableName} = <@includeModel object=valueConversion/>;
diff --git a/processor/src/main/resources/org.mapstruct.ap.model.MethodReference.ftl b/processor/src/main/resources/org.mapstruct.ap.model.MethodReference.ftl
index 9de44aa8d..485aa58bb 100644
--- a/processor/src/main/resources/org.mapstruct.ap.model.MethodReference.ftl
+++ b/processor/src/main/resources/org.mapstruct.ap.model.MethodReference.ftl
@@ -18,4 +18,8 @@
limitations under the License.
-->
-<#if declaringMapper??>${mapperVariableName}.#if>${name}<#if ext.input??>( <#if methodRefChild??><@includeModel object=methodRefChild input=ext.input/><#if contextParam??>, ${contextParam}#if><#else>${ext.input}<#if contextParam??>, ${contextParam}#if>#if> )<#else>()#if>
\ No newline at end of file
+<#if methodRefChild??>
+ <#if declaringMapper??>${mapperVariableName}.#if>${name}(<#list parameters as param> <#if param.targetType>${ext.targetType}.class<#else><@includeModel object=methodRefChild input=ext.input targetType=singleSourceParameterType.name/>#if><#if param_has_next>,<#else> #if>#list><#if contextParam??>, ${contextParam}#if>)<#t>
+<#else>
+ <#if declaringMapper??>${mapperVariableName}.#if>${name}(<#list parameters as param> <#if param.targetType>${ext.targetType}.class<#else>${ext.input}#if><#if param_has_next>,<#else> #if>#list><#if contextParam??>, ${contextParam}#if>)<#t>
+#if>
\ No newline at end of file
diff --git a/processor/src/main/resources/org.mapstruct.ap.model.PropertyMapping.ftl b/processor/src/main/resources/org.mapstruct.ap.model.PropertyMapping.ftl
index 785feaeb7..759ad5a56 100644
--- a/processor/src/main/resources/org.mapstruct.ap.model.PropertyMapping.ftl
+++ b/processor/src/main/resources/org.mapstruct.ap.model.PropertyMapping.ftl
@@ -21,10 +21,10 @@
<#-- a) invoke mapping method -->
<#if mappingMethod??>
<#if targetAccessorSetter>
-${ext.targetBeanName}.${targetAccessorName}( <@includeModel object=mappingMethod input="${sourceBeanName}.${sourceAccessorName}()"/> );
+${ext.targetBeanName}.${targetAccessorName}( <@includeModel object=mappingMethod input="${sourceBeanName}.${sourceAccessorName}()" targetType="${targetType.name}"/> );
<#elseif targetType.collectionType>
if ( ${sourceBeanName}.${sourceAccessorName}() != null && ${ext.targetBeanName}.${targetAccessorName}() != null ) {
- ${ext.targetBeanName}.${targetAccessorName}().addAll( <@includeModel object=mappingMethod input="${sourceBeanName}.${sourceAccessorName}()"/> );
+ ${ext.targetBeanName}.${targetAccessorName}().addAll( <@includeModel object=mappingMethod input="${sourceBeanName}.${sourceAccessorName}()" targetType="${targetType.name}"/> );
}
#if>
<#-- b) simple conversion -->
diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/typemismatch/ErroneousMapper.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/typemismatch/ErroneousMapper.java
index 9636407bc..d869c746e 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/erroneous/typemismatch/ErroneousMapper.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/typemismatch/ErroneousMapper.java
@@ -19,6 +19,7 @@
package org.mapstruct.ap.test.erroneous.typemismatch;
import org.mapstruct.Mapper;
+import org.mapstruct.TargetType;
@Mapper
public interface ErroneousMapper {
@@ -30,4 +31,6 @@ public interface ErroneousMapper {
long sourceToLong(Source source);
Source longToSource(long id);
+
+ Target sourceToTargetWithMappingTargetType(Source source, @TargetType Class> clazz);
}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/erroneous/typemismatch/ErroneousMappingsTest.java b/processor/src/test/java/org/mapstruct/ap/test/erroneous/typemismatch/ErroneousMappingsTest.java
index 7b72f2f9a..5690d03c3 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/erroneous/typemismatch/ErroneousMappingsTest.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/erroneous/typemismatch/ErroneousMappingsTest.java
@@ -43,20 +43,25 @@ public class ErroneousMappingsTest extends MapperTestBase {
diagnostics = {
@Diagnostic(type = ErroneousMapper.class,
kind = Kind.ERROR,
- line = 26,
+ line = 27,
messageRegExp = "Can't map property \"boolean foo\" to \"int foo\"\\."),
@Diagnostic(type = ErroneousMapper.class,
kind = Kind.ERROR,
- line = 28,
+ line = 29,
messageRegExp = "Can't map property \"int foo\" to \"boolean foo\"\\."),
@Diagnostic(type = ErroneousMapper.class,
kind = Kind.ERROR,
- line = 30,
+ line = 31,
messageRegExp = "Can't generate mapping method with primitive return type\\."),
@Diagnostic(type = ErroneousMapper.class,
kind = Kind.ERROR,
- line = 32,
- messageRegExp = "Can't generate mapping method with primitive parameter type\\.")
+ line = 33,
+ messageRegExp = "Can't generate mapping method with primitive parameter type\\."),
+ @Diagnostic(type = ErroneousMapper.class,
+ kind = Kind.ERROR,
+ line = 35,
+ messageRegExp =
+ "Can't generate mapping method that has a parameter annotated with @TargetType\\.")
}
)
public void shouldFailToGenerateMappings() {
diff --git a/processor/src/test/java/org/mapstruct/ap/test/factories/Bar1.java b/processor/src/test/java/org/mapstruct/ap/test/factories/Bar1.java
index 9d4f32a58..2a69af0d2 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/factories/Bar1.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/factories/Bar1.java
@@ -21,7 +21,7 @@ package org.mapstruct.ap.test.factories;
/**
* @author Sjaak Derksen
*/
-public class Bar1 {
+public class Bar1 implements FactoryCreatable {
private String prop;
private final String someTypeProp;
diff --git a/processor/src/test/java/org/mapstruct/ap/test/factories/FactoryCreatable.java b/processor/src/test/java/org/mapstruct/ap/test/factories/FactoryCreatable.java
new file mode 100644
index 000000000..ad7edbbec
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/factories/FactoryCreatable.java
@@ -0,0 +1,27 @@
+/**
+ * Copyright 2012-2014 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.ap.test.factories;
+
+/**
+ * @author Andreas Gudian
+ *
+ */
+public interface FactoryCreatable {
+ void setProp(String prop);
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/factories/FactoryTest.java b/processor/src/test/java/org/mapstruct/ap/test/factories/FactoryTest.java
index 38c85b61c..c67c158ad 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/factories/FactoryTest.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/factories/FactoryTest.java
@@ -18,6 +18,8 @@
*/
package org.mapstruct.ap.test.factories;
+import static org.fest.assertions.Assertions.assertThat;
+
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -29,8 +31,6 @@ import org.mapstruct.ap.testutil.MapperTestBase;
import org.mapstruct.ap.testutil.WithClasses;
import org.testng.annotations.Test;
-import static org.fest.assertions.Assertions.assertThat;
-
/**
* @author Sjaak Derksen
*/
@@ -38,7 +38,7 @@ import static org.fest.assertions.Assertions.assertThat;
@WithClasses({
Bar1.class, Foo1.class, Bar2.class, Foo2.class, Bar3.class, Foo3.class, BarFactory.class,
org.mapstruct.ap.test.factories.b.BarFactory.class, Source.class, SourceTargetMapperAndBar2Factory.class,
- Target.class, CustomList.class, CustomListImpl.class, CustomMap.class, CustomMapImpl.class
+ Target.class, CustomList.class, CustomListImpl.class, CustomMap.class, CustomMapImpl.class, FactoryCreatable.class
})
public class FactoryTest extends MapperTestBase {
@Test
@@ -87,4 +87,19 @@ public class FactoryTest extends MapperTestBase {
source.setPropMap( fooMap );
return source;
}
+
+ @Test
+ @IssueKey( "136" )
+ @WithClasses( { GenericFactory.class, SourceTargetMapperWithGenericFactory.class } )
+ public void shouldUseGenericFactory() {
+ SourceTargetMapperWithGenericFactory mapper = SourceTargetMapperWithGenericFactory.INSTANCE;
+
+ Foo1 foo1 = new Foo1();
+ foo1.setProp( "foo1" );
+ Bar1 bar1 = mapper.fromFoo1( foo1 );
+
+ assertThat( bar1 ).isNotNull();
+ assertThat( bar1.getSomeTypeProp() ).isEqualTo( "created by GenericFactory" );
+ assertThat( bar1.getProp() ).isEqualTo( "foo1" );
+ }
}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/factories/GenericFactory.java b/processor/src/test/java/org/mapstruct/ap/test/factories/GenericFactory.java
new file mode 100644
index 000000000..d9592e3b9
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/factories/GenericFactory.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright 2012-2014 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.ap.test.factories;
+
+import org.mapstruct.TargetType;
+
+/**
+ * @author Andreas Gudian
+ *
+ */
+public class GenericFactory {
+ @SuppressWarnings( "unchecked" )
+ public T createNew(@TargetType Class clazz) {
+ if ( clazz == Bar1.class ) {
+ return (T) new Bar1( "created by GenericFactory" );
+ }
+
+ return null;
+ }
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/factories/SourceTargetMapperWithGenericFactory.java b/processor/src/test/java/org/mapstruct/ap/test/factories/SourceTargetMapperWithGenericFactory.java
new file mode 100644
index 000000000..d9ab7ae3d
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/factories/SourceTargetMapperWithGenericFactory.java
@@ -0,0 +1,33 @@
+/**
+ * Copyright 2012-2014 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.ap.test.factories;
+
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+/**
+ * @author Andreas Gudian
+ *
+ */
+@Mapper( uses = { GenericFactory.class } )
+public interface SourceTargetMapperWithGenericFactory {
+ SourceTargetMapperWithGenericFactory INSTANCE = Mappers.getMapper( SourceTargetMapperWithGenericFactory.class );
+
+ Bar1 fromFoo1(Foo1 foo1);
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/references/BaseType.java b/processor/src/test/java/org/mapstruct/ap/test/references/BaseType.java
new file mode 100644
index 000000000..3f1c7f920
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/references/BaseType.java
@@ -0,0 +1,27 @@
+/**
+ * Copyright 2012-2014 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.ap.test.references;
+
+/**
+ * @author Andreas Gudian
+ *
+ */
+public class BaseType {
+
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/references/ReferencedCustomMapper.java b/processor/src/test/java/org/mapstruct/ap/test/references/ReferencedCustomMapper.java
index 57ff5620b..00e11d9ab 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/references/ReferencedCustomMapper.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/references/ReferencedCustomMapper.java
@@ -18,6 +18,10 @@
*/
package org.mapstruct.ap.test.references;
+import java.util.Map;
+
+import org.mapstruct.TargetType;
+
/**
* @author Andreas Gudian
*
@@ -26,4 +30,24 @@ public class ReferencedCustomMapper {
public long incrementingIntToLong(int source) {
return source + 1;
}
+
+ @SuppressWarnings( "unchecked" )
+ public T convert(String string, @TargetType Class clazz) {
+ if ( clazz == SomeType.class ) {
+ return (T) new SomeType( string );
+ }
+ else if ( clazz == SomeOtherType.class ) {
+ return (T) new SomeOtherType( string );
+ }
+
+ return null;
+ }
+
+ /**
+ * This method should not be chosen for the mapping, as our types are never within the bounds of
+ * {@code T extends Map,?>}
+ */
+ public > T unused(String string, @TargetType Class clazz) {
+ throw new RuntimeException( "should never be called" );
+ }
}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/references/ReferencedMapperTest.java b/processor/src/test/java/org/mapstruct/ap/test/references/ReferencedMapperTest.java
index 9233a492c..6c8486511 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/references/ReferencedMapperTest.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/references/ReferencedMapperTest.java
@@ -19,6 +19,12 @@
package org.mapstruct.ap.test.references;
import static org.fest.assertions.Assertions.assertThat;
+import static org.fest.assertions.MapAssert.entry;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.MapperTestBase;
@@ -31,7 +37,7 @@ import org.testng.annotations.Test;
*/
@IssueKey( "82" )
@WithClasses( { Bar.class, Foo.class, FooMapper.class, ReferencedCustomMapper.class, Source.class,
- SourceTargetMapper.class, Target.class } )
+ SourceTargetMapper.class, Target.class, BaseType.class, SomeType.class, SomeOtherType.class } )
public class ReferencedMapperTest extends MapperTestBase {
@Test
public void referencedMappersAreInstatiatedCorrectly() {
@@ -41,6 +47,7 @@ public class ReferencedMapperTest extends MapperTestBase {
assertThat( target.getProp1() ).isEqualTo( 43 );
assertThat( target.getProp2() ).isNotNull();
assertThat( target.getProp2().getProp1() ).isEqualTo( "foo" );
+ assertThat( target.getProp3().getValue() ).isEqualTo( "prop3" );
}
private Source createSource() {
@@ -50,7 +57,46 @@ public class ReferencedMapperTest extends MapperTestBase {
Foo prop2 = new Foo();
prop2.setProp1( "foo" );
source.setProp2( prop2 );
+ source.setProp3( "prop3" );
return source;
}
+
+ @Test
+ @IssueKey( "136" )
+ public void shouldUseGenericFactoryForIterable() {
+ List result = SourceTargetMapper.INSTANCE.fromStringList( Arrays.asList( "foo1", "foo2" ) );
+
+ assertThat( result ).onProperty( "value" ).containsExactly( "foo1", "foo2" );
+ }
+
+ @Test
+ @IssueKey( "136" )
+ public void shouldUseGenericFactoryForMap() {
+ Map source = new HashMap();
+ source.put( "foo1", "bar1" );
+ source.put( "foo2", "bar2" );
+ Map result = SourceTargetMapper.INSTANCE.fromStringMap( source );
+
+ assertThat( result ).hasSize( 2 );
+ assertThat( result ).includes(
+ entry( new SomeType( "foo1" ), new SomeOtherType( "bar1" ) ),
+ entry( new SomeType( "foo2" ), new SomeOtherType( "bar2" ) ) );
+ }
+
+ @Test
+ @IssueKey( "136" )
+ @WithClasses( { SourceTargetMapperWithPrimitives.class, SourceWithWrappers.class, TargetWithPrimitives.class } )
+ public void shouldMapPrimitivesWithCustomMapper() {
+ SourceWithWrappers source = new SourceWithWrappers();
+ source.setProp1( new SomeType( "42" ) );
+ source.setProp2( new SomeType( "1701" ) );
+ source.setProp3( new SomeType( "true" ) );
+
+ TargetWithPrimitives result = SourceTargetMapperWithPrimitives.INSTANCE.sourceToTarget( source );
+
+ assertThat( result.getProp1() ).isEqualTo( 42 );
+ assertThat( result.getProp2() ).isEqualTo( 1701 );
+ assertThat( result.isProp3() ).isEqualTo( true );
+ }
}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/references/SomeOtherType.java b/processor/src/test/java/org/mapstruct/ap/test/references/SomeOtherType.java
new file mode 100644
index 000000000..dd0bba24b
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/references/SomeOtherType.java
@@ -0,0 +1,70 @@
+/**
+ * Copyright 2012-2014 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.ap.test.references;
+
+/**
+ * @author Andreas Gudian
+ *
+ */
+public class SomeOtherType extends BaseType {
+ private String value;
+
+ public SomeOtherType(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ( ( value == null ) ? 0 : value.hashCode() );
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if ( this == obj ) {
+ return true;
+ }
+ if ( obj == null ) {
+ return false;
+ }
+ if ( getClass() != obj.getClass() ) {
+ return false;
+ }
+ SomeOtherType other = (SomeOtherType) obj;
+ if ( value == null ) {
+ if ( other.value != null ) {
+ return false;
+ }
+ }
+ else if ( !value.equals( other.value ) ) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/references/SomeType.java b/processor/src/test/java/org/mapstruct/ap/test/references/SomeType.java
new file mode 100644
index 000000000..072e23e15
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/references/SomeType.java
@@ -0,0 +1,70 @@
+/**
+ * Copyright 2012-2014 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.ap.test.references;
+
+/**
+ * @author Andreas Gudian
+ *
+ */
+public class SomeType extends BaseType {
+ private String value;
+
+ public SomeType(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ( ( value == null ) ? 0 : value.hashCode() );
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if ( this == obj ) {
+ return true;
+ }
+ if ( obj == null ) {
+ return false;
+ }
+ if ( getClass() != obj.getClass() ) {
+ return false;
+ }
+ SomeType other = (SomeType) obj;
+ if ( value == null ) {
+ if ( other.value != null ) {
+ return false;
+ }
+ }
+ else if ( !value.equals( other.value ) ) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/references/Source.java b/processor/src/test/java/org/mapstruct/ap/test/references/Source.java
index 9051df79e..b3b11793f 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/references/Source.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/references/Source.java
@@ -25,6 +25,7 @@ package org.mapstruct.ap.test.references;
public class Source {
private int prop1;
private Foo prop2;
+ private String prop3;
public int getProp1() {
return prop1;
@@ -41,4 +42,12 @@ public class Source {
public void setProp2(Foo prop2) {
this.prop2 = prop2;
}
+
+ public String getProp3() {
+ return prop3;
+ }
+
+ public void setProp3(String prop3) {
+ this.prop3 = prop3;
+ }
}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/references/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/references/SourceTargetMapper.java
index ee66db22a..e47333b02 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/references/SourceTargetMapper.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/references/SourceTargetMapper.java
@@ -18,6 +18,9 @@
*/
package org.mapstruct.ap.test.references;
+import java.util.List;
+import java.util.Map;
+
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@@ -30,4 +33,8 @@ public interface SourceTargetMapper {
SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class );
Target sourceToTarget(Source source);
+
+ List fromStringList(List stringList);
+
+ Map fromStringMap(Map stringStringMap);
}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/references/SourceTargetMapperWithPrimitives.java b/processor/src/test/java/org/mapstruct/ap/test/references/SourceTargetMapperWithPrimitives.java
new file mode 100644
index 000000000..e3189b0c9
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/references/SourceTargetMapperWithPrimitives.java
@@ -0,0 +1,50 @@
+/**
+ * Copyright 2012-2014 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.ap.test.references;
+
+import org.mapstruct.Mapper;
+import org.mapstruct.TargetType;
+import org.mapstruct.factory.Mappers;
+
+/**
+ * @author Andreas Gudian
+ *
+ */
+@Mapper
+public abstract class SourceTargetMapperWithPrimitives {
+ public static final SourceTargetMapperWithPrimitives INSTANCE =
+ Mappers.getMapper( SourceTargetMapperWithPrimitives.class );
+
+ public abstract TargetWithPrimitives sourceToTarget(SourceWithWrappers source);
+
+ @SuppressWarnings( "unchecked" )
+ public T convert(@TargetType Class clazz, SomeType wrapper) {
+ if ( clazz == int.class ) {
+ return (T) Integer.valueOf( wrapper.getValue() );
+ }
+ else if ( clazz == long.class ) {
+ return (T) Long.valueOf( wrapper.getValue() );
+ }
+ else if ( clazz == boolean.class ) {
+ return (T) Boolean.valueOf( wrapper.getValue() );
+ }
+
+ return null;
+ }
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/references/SourceWithWrappers.java b/processor/src/test/java/org/mapstruct/ap/test/references/SourceWithWrappers.java
new file mode 100644
index 000000000..4b9a81968
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/references/SourceWithWrappers.java
@@ -0,0 +1,54 @@
+/**
+ * Copyright 2012-2014 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.ap.test.references;
+
+/**
+ * @author Andreas Gudian
+ *
+ */
+public class SourceWithWrappers {
+ private SomeType prop1;
+ private SomeType prop2;
+ private SomeType prop3;
+
+ public SomeType getProp1() {
+ return prop1;
+ }
+
+ public void setProp1(SomeType prop1) {
+ this.prop1 = prop1;
+ }
+
+ public SomeType getProp2() {
+ return prop2;
+ }
+
+ public void setProp2(SomeType prop2) {
+ this.prop2 = prop2;
+ }
+
+ public SomeType getProp3() {
+ return prop3;
+ }
+
+ public void setProp3(SomeType prop3) {
+ this.prop3 = prop3;
+ }
+
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/references/Target.java b/processor/src/test/java/org/mapstruct/ap/test/references/Target.java
index 0aa12b397..de4406bb7 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/references/Target.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/references/Target.java
@@ -18,6 +18,7 @@
*/
package org.mapstruct.ap.test.references;
+
/**
* @author Andreas Gudian
*
@@ -25,6 +26,7 @@ package org.mapstruct.ap.test.references;
public class Target {
private long prop1;
private Bar prop2;
+ private SomeType prop3;
public long getProp1() {
return prop1;
@@ -41,4 +43,12 @@ public class Target {
public void setProp2(Bar prop2) {
this.prop2 = prop2;
}
+
+ public SomeType getProp3() {
+ return prop3;
+ }
+
+ public void setProp3(SomeType prop3) {
+ this.prop3 = prop3;
+ }
}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/references/TargetWithPrimitives.java b/processor/src/test/java/org/mapstruct/ap/test/references/TargetWithPrimitives.java
new file mode 100644
index 000000000..12fb466c0
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/references/TargetWithPrimitives.java
@@ -0,0 +1,53 @@
+/**
+ * Copyright 2012-2014 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.ap.test.references;
+
+/**
+ * @author Andreas Gudian
+ *
+ */
+public class TargetWithPrimitives {
+ private int prop1;
+ private long prop2;
+ private boolean prop3;
+
+ public int getProp1() {
+ return prop1;
+ }
+
+ public void setProp1(int prop1) {
+ this.prop1 = prop1;
+ }
+
+ public long getProp2() {
+ return prop2;
+ }
+
+ public void setProp2(long prop2) {
+ this.prop2 = prop2;
+ }
+
+ public boolean isProp3() {
+ return prop3;
+ }
+
+ public void setProp3(boolean prop3) {
+ this.prop3 = prop3;
+ }
+}