mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#136 Introduce @TargetType to pass the target type of a custom property mapping method or factory method as parameter to the method
This commit is contained in:
parent
8ddb2a9fb3
commit
4372e726ee
@ -25,9 +25,6 @@ import java.lang.annotation.Target;
|
|||||||
* Declares a parameter of a mapping method to be the target of the mapping.
|
* Declares a parameter of a mapping method to be the target of the mapping.
|
||||||
* <p>
|
* <p>
|
||||||
* Not more than one parameter can be declared as {@code MappingTarget}.
|
* Not more than one parameter can be declared as {@code MappingTarget}.
|
||||||
* <p>
|
|
||||||
* 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
|
* @author Andreas Gudian
|
||||||
*/
|
*/
|
||||||
|
34
core/src/main/java/org/mapstruct/TargetType.java
Normal file
34
core/src/main/java/org/mapstruct/TargetType.java
Normal file
@ -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.
|
||||||
|
* <p>
|
||||||
|
* 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 {
|
||||||
|
}
|
@ -21,7 +21,9 @@ package org.mapstruct.ap.model;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.mapstruct.ap.model.common.ConversionContext;
|
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.common.Type;
|
||||||
|
import org.mapstruct.ap.model.source.Method;
|
||||||
import org.mapstruct.ap.model.source.SourceMethod;
|
import org.mapstruct.ap.model.source.SourceMethod;
|
||||||
import org.mapstruct.ap.model.source.builtin.BuiltInMethod;
|
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 {
|
public class MethodReference extends MappingMethod {
|
||||||
|
|
||||||
private final MapperReference declaringMapper;
|
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<Bar>} to
|
* A reference to another mapping method in case this is a two-step mapping, e.g. from {@code JAXBElement<Bar>} to
|
||||||
@ -52,12 +55,21 @@ public class MethodReference extends MappingMethod {
|
|||||||
super( method );
|
super( method );
|
||||||
this.declaringMapper = declaringMapper;
|
this.declaringMapper = declaringMapper;
|
||||||
this.contextParam = null;
|
this.contextParam = null;
|
||||||
|
this.importType = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MethodReference(BuiltInMethod method, ConversionContext contextParam) {
|
public MethodReference(BuiltInMethod method, ConversionContext contextParam) {
|
||||||
super( method );
|
super( method );
|
||||||
this.declaringMapper = null;
|
this.declaringMapper = null;
|
||||||
this.contextParam = method.getContextParameter( contextParam );
|
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() {
|
public MapperReference getDeclaringMapper() {
|
||||||
@ -72,6 +84,18 @@ public class MethodReference extends MappingMethod {
|
|||||||
return contextParam;
|
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) {
|
public void setMethodRefChild(MethodReference methodRefChild) {
|
||||||
this.methodRefChild = methodRefChild;
|
this.methodRefChild = methodRefChild;
|
||||||
}
|
}
|
||||||
@ -83,6 +107,9 @@ public class MethodReference extends MappingMethod {
|
|||||||
@Override
|
@Override
|
||||||
public Set<Type> getImportTypes() {
|
public Set<Type> getImportTypes() {
|
||||||
Set<Type> imported = super.getImportTypes();
|
Set<Type> imported = super.getImportTypes();
|
||||||
|
if ( importType != null ) {
|
||||||
|
imported.add( importType );
|
||||||
|
}
|
||||||
if ( methodRefChild != null ) {
|
if ( methodRefChild != null ) {
|
||||||
imported.addAll( methodRefChild.getImportTypes() );
|
imported.addAll( methodRefChild.getImportTypes() );
|
||||||
}
|
}
|
||||||
|
@ -32,15 +32,17 @@ public class Parameter extends ModelElement {
|
|||||||
private final String name;
|
private final String name;
|
||||||
private final Type type;
|
private final Type type;
|
||||||
private final boolean mappingTarget;
|
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.name = name;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.mappingTarget = mappingTarget;
|
this.mappingTarget = mappingTarget;
|
||||||
|
this.targetType = targetType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Parameter(String name, Type type) {
|
public Parameter(String name, Type type) {
|
||||||
this( name, type, false );
|
this( name, type, false, false );
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
@ -57,11 +59,16 @@ public class Parameter extends ModelElement {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return ( mappingTarget ? "@MappingTarget " : "" ) + type.toString() + " " + name;
|
return ( mappingTarget ? "@MappingTarget " : "" ) + ( targetType ? "@TargetType " : "" )
|
||||||
|
+ type.toString() + " " + name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<Type> getImportTypes() {
|
public Set<Type> getImportTypes() {
|
||||||
return Collections.asSet( type );
|
return Collections.asSet( type );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isTargetType() {
|
||||||
|
return targetType;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.lang.model.element.AnnotationMirror;
|
import javax.lang.model.element.AnnotationMirror;
|
||||||
import javax.lang.model.element.Element;
|
import javax.lang.model.element.Element;
|
||||||
import javax.lang.model.element.ElementKind;
|
import javax.lang.model.element.ElementKind;
|
||||||
|
@ -41,6 +41,7 @@ import javax.lang.model.element.ExecutableElement;
|
|||||||
import javax.lang.model.element.TypeElement;
|
import javax.lang.model.element.TypeElement;
|
||||||
import javax.lang.model.element.VariableElement;
|
import javax.lang.model.element.VariableElement;
|
||||||
import javax.lang.model.type.DeclaredType;
|
import javax.lang.model.type.DeclaredType;
|
||||||
|
import javax.lang.model.type.PrimitiveType;
|
||||||
import javax.lang.model.type.TypeKind;
|
import javax.lang.model.type.TypeKind;
|
||||||
import javax.lang.model.type.TypeMirror;
|
import javax.lang.model.type.TypeMirror;
|
||||||
import javax.lang.model.util.Elements;
|
import javax.lang.model.util.Elements;
|
||||||
@ -48,6 +49,7 @@ import javax.lang.model.util.SimpleElementVisitor6;
|
|||||||
import javax.lang.model.util.Types;
|
import javax.lang.model.util.Types;
|
||||||
|
|
||||||
import org.mapstruct.ap.prism.MappingTargetPrism;
|
import org.mapstruct.ap.prism.MappingTargetPrism;
|
||||||
|
import org.mapstruct.ap.prism.TargetTypePrism;
|
||||||
import org.mapstruct.ap.util.AnnotationProcessingException;
|
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. <br />
|
||||||
|
* Examples: <br />
|
||||||
|
* If type represents {@code java.lang.Integer}, it will return the type that represents {@code Class<Integer>}.
|
||||||
|
* <br />
|
||||||
|
* If type represents {@code int}, it will return the type that represents {@code Class<Integer>}.
|
||||||
|
*
|
||||||
|
* @param type the type to return the declared class type for
|
||||||
|
* @return the type representing {@code Class<type>}.
|
||||||
|
*/
|
||||||
|
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) {
|
public Parameter getSingleParameter(ExecutableElement method) {
|
||||||
List<? extends VariableElement> parameters = method.getParameters();
|
List<? extends VariableElement> parameters = method.getParameters();
|
||||||
|
|
||||||
@ -197,8 +222,7 @@ public class TypeFactory {
|
|||||||
|
|
||||||
return new Parameter(
|
return new Parameter(
|
||||||
parameter.getSimpleName().toString(),
|
parameter.getSimpleName().toString(),
|
||||||
getType( parameter.asType() ),
|
getType( parameter.asType() )
|
||||||
false
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,7 +236,8 @@ public class TypeFactory {
|
|||||||
new Parameter(
|
new Parameter(
|
||||||
parameter.getSimpleName().toString(),
|
parameter.getSimpleName().toString(),
|
||||||
getType( parameter.asType() ),
|
getType( parameter.asType() ),
|
||||||
MappingTargetPrism.getInstanceOn( parameter ) != null
|
MappingTargetPrism.getInstanceOn( parameter ) != null,
|
||||||
|
TargetTypePrism.getInstanceOn( parameter ) != null
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -36,15 +36,14 @@ import org.mapstruct.ap.model.source.builtin.BuiltInMethod;
|
|||||||
public interface Method {
|
public interface Method {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether the provided sourceType and provided targetType match with the parameter respectively
|
* Checks whether the provided sourceType and provided targetType match with the parameter respectively return type
|
||||||
* return type of the method. The check also should incorporate wild card and generic type variables
|
* 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
|
* @param targetType the targetType to match to the returnType
|
||||||
*
|
|
||||||
* @return true when match
|
* @return true when match
|
||||||
*/
|
*/
|
||||||
boolean matches(Type sourceType, Type targetType);
|
boolean matches(List<Type> sourceTypes, Type targetType);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the mapper type declaring this method if it is not declared by the mapper interface currently processed
|
* Returns the mapper type declaring this method if it is not declared by the mapper interface currently processed
|
||||||
|
@ -21,6 +21,7 @@ package org.mapstruct.ap.model.source;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.lang.model.element.TypeElement;
|
import javax.lang.model.element.TypeElement;
|
||||||
import javax.lang.model.element.TypeParameterElement;
|
import javax.lang.model.element.TypeParameterElement;
|
||||||
import javax.lang.model.element.VariableElement;
|
import javax.lang.model.element.VariableElement;
|
||||||
@ -61,10 +62,8 @@ import org.mapstruct.ap.model.common.Type;
|
|||||||
*/
|
*/
|
||||||
public class MethodMatcher {
|
public class MethodMatcher {
|
||||||
|
|
||||||
|
|
||||||
private final SourceMethod candidateMethod;
|
private final SourceMethod candidateMethod;
|
||||||
private final Types typeUtils;
|
private final Types typeUtils;
|
||||||
private final Map<TypeVariable, TypeMirror> genericTypesMap = new HashMap<TypeVariable, TypeMirror>();
|
|
||||||
|
|
||||||
MethodMatcher(Types typeUtils, SourceMethod candidateMethod) {
|
MethodMatcher(Types typeUtils, SourceMethod candidateMethod) {
|
||||||
this.typeUtils = typeUtils;
|
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
|
* @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.
|
* {@code false} otherwise.
|
||||||
*/
|
*/
|
||||||
boolean matches(Type sourceType, Type targetType) {
|
boolean matches(List<Type> sourceTypes, Type targetType) {
|
||||||
// check & collect generic types.
|
// check & collect generic types.
|
||||||
List<? extends VariableElement> candidateParameters = candidateMethod.getExecutable().getParameters();
|
List<? extends VariableElement> candidateParameters = candidateMethod.getExecutable().getParameters();
|
||||||
|
|
||||||
if ( candidateParameters.size() != 1 ) {
|
if ( candidateParameters.size() != sourceTypes.size() ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeMatcher parameterMatcher = new TypeMatcher( Assignability.VISITED_ASSIGNABLE_FROM );
|
Map<TypeVariable, TypeMirror> genericTypesMap = new HashMap<TypeVariable, TypeMirror>();
|
||||||
if ( !parameterMatcher.visit( candidateParameters.iterator().next().asType(), sourceType.getTypeMirror() ) ) {
|
|
||||||
return false;
|
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
|
// check return type
|
||||||
TypeMirror candidateReturnType = candidateMethod.getExecutable().getReturnType();
|
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() ) ) {
|
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
|
// 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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if all entries are in the bounds
|
// check if all entries are in the bounds
|
||||||
for ( Map.Entry<TypeVariable, TypeMirror> entry : genericTypesMap.entrySet() ) {
|
for ( Map.Entry<TypeVariable, TypeMirror> entry : genericTypesMap.entrySet() ) {
|
||||||
if ( !isWithinBounds( entry.getValue(), getTypeParamFromCandidate( entry.getKey() ) ) ) {
|
if ( !isWithinBounds( entry.getValue(), getTypeParamFromCandidate( entry.getKey() ) ) ) {
|
||||||
@ -120,10 +137,12 @@ public class MethodMatcher {
|
|||||||
|
|
||||||
private class TypeMatcher extends SimpleTypeVisitor6<Boolean, TypeMirror> {
|
private class TypeMatcher extends SimpleTypeVisitor6<Boolean, TypeMirror> {
|
||||||
private final Assignability assignability;
|
private final Assignability assignability;
|
||||||
|
private final Map<TypeVariable, TypeMirror> genericTypesMap;
|
||||||
|
|
||||||
public TypeMatcher(Assignability assignability) {
|
public TypeMatcher(Assignability assignability, Map<TypeVariable, TypeMirror> genericTypesMap) {
|
||||||
super( Boolean.FALSE ); // default value
|
super( Boolean.FALSE ); // default value
|
||||||
this.assignability = assignability;
|
this.assignability = assignability;
|
||||||
|
this.genericTypesMap = genericTypesMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -22,6 +22,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.lang.model.element.ExecutableElement;
|
import javax.lang.model.element.ExecutableElement;
|
||||||
import javax.lang.model.element.Modifier;
|
import javax.lang.model.element.Modifier;
|
||||||
import javax.lang.model.util.Types;
|
import javax.lang.model.util.Types;
|
||||||
@ -58,11 +59,9 @@ public class SourceMethod implements Method {
|
|||||||
private boolean configuredByReverseMappingMethod = false;
|
private boolean configuredByReverseMappingMethod = false;
|
||||||
|
|
||||||
public static SourceMethod forMethodRequiringImplementation(ExecutableElement executable,
|
public static SourceMethod forMethodRequiringImplementation(ExecutableElement executable,
|
||||||
List<Parameter> parameters,
|
List<Parameter> parameters, Type returnType,
|
||||||
Type returnType, Map<String,
|
Map<String, List<Mapping>> mappings,
|
||||||
List<Mapping>> mappings,
|
IterableMapping iterableMapping, MapMapping mapMapping,
|
||||||
IterableMapping iterableMapping,
|
|
||||||
MapMapping mapMapping,
|
|
||||||
Types typeUtils) {
|
Types typeUtils) {
|
||||||
|
|
||||||
return new SourceMethod(
|
return new SourceMethod(
|
||||||
@ -73,51 +72,40 @@ public class SourceMethod implements Method {
|
|||||||
mappings,
|
mappings,
|
||||||
iterableMapping,
|
iterableMapping,
|
||||||
mapMapping,
|
mapMapping,
|
||||||
typeUtils
|
typeUtils );
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SourceMethod forReferencedMethod(Type declaringMapper,
|
public static SourceMethod forReferencedMethod(Type declaringMapper, ExecutableElement executable,
|
||||||
ExecutableElement executable,
|
List<Parameter> parameters, Type returnType, Types typeUtils) {
|
||||||
List<Parameter> parameters,
|
|
||||||
Type returnType,
|
|
||||||
Types typeUtils) {
|
|
||||||
|
|
||||||
return new SourceMethod(
|
return new SourceMethod(
|
||||||
declaringMapper,
|
declaringMapper,
|
||||||
executable,
|
executable,
|
||||||
parameters,
|
parameters,
|
||||||
returnType,
|
returnType,
|
||||||
Collections.<String, List<Mapping>>emptyMap(),
|
Collections.<String, List<Mapping>> emptyMap(),
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
typeUtils
|
typeUtils );
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SourceMethod forFactoryMethod(Type declaringMapper, ExecutableElement executable,
|
public static SourceMethod forFactoryMethod(Type declaringMapper, ExecutableElement executable, Type returnType,
|
||||||
Type returnType, Types typeUtils) {
|
Types typeUtils) {
|
||||||
|
|
||||||
return new SourceMethod(
|
return new SourceMethod(
|
||||||
declaringMapper,
|
declaringMapper,
|
||||||
executable,
|
executable,
|
||||||
Collections.<Parameter>emptyList(),
|
Collections.<Parameter> emptyList(),
|
||||||
returnType,
|
returnType,
|
||||||
Collections.<String, List<Mapping>>emptyMap(),
|
Collections.<String, List<Mapping>> emptyMap(),
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
typeUtils
|
typeUtils );
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private SourceMethod(Type declaringMapper,
|
private SourceMethod(Type declaringMapper, ExecutableElement executable, List<Parameter> parameters,
|
||||||
ExecutableElement executable,
|
Type returnType, Map<String, List<Mapping>> mappings, IterableMapping iterableMapping,
|
||||||
List<Parameter> parameters,
|
MapMapping mapMapping, Types typeUtils) {
|
||||||
Type returnType,
|
|
||||||
Map<String, List<Mapping>> mappings,
|
|
||||||
IterableMapping iterableMapping,
|
|
||||||
MapMapping mapMapping,
|
|
||||||
Types typeUtils) {
|
|
||||||
this.declaringMapper = declaringMapper;
|
this.declaringMapper = declaringMapper;
|
||||||
this.executable = executable;
|
this.executable = executable;
|
||||||
this.parameters = parameters;
|
this.parameters = parameters;
|
||||||
@ -178,7 +166,7 @@ public class SourceMethod implements Method {
|
|||||||
List<Parameter> sourceParameters = new ArrayList<Parameter>();
|
List<Parameter> sourceParameters = new ArrayList<Parameter>();
|
||||||
|
|
||||||
for ( Parameter parameter : parameters ) {
|
for ( Parameter parameter : parameters ) {
|
||||||
if ( !parameter.isMappingTarget() ) {
|
if ( !parameter.isMappingTarget() && !parameter.isTargetType() ) {
|
||||||
sourceParameters.add( parameter );
|
sourceParameters.add( parameter );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -336,9 +324,22 @@ public class SourceMethod implements Method {
|
|||||||
* {@inheritDoc} {@link Method}
|
* {@inheritDoc} {@link Method}
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean matches(Type sourceType, Type targetType) {
|
public boolean matches(List<Type> sourceTypes, Type targetType) {
|
||||||
MethodMatcher matcher = new MethodMatcher( typeUtils, this );
|
MethodMatcher matcher = new MethodMatcher( typeUtils, this );
|
||||||
return matcher.matches( sourceType, targetType );
|
return matcher.matches( sourceTypes, targetType );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param parameters the parameter list to check
|
||||||
|
* @return <code>true</code>, iff the parameter list contains a parameter annotated with {@code @TargetType}
|
||||||
|
*/
|
||||||
|
public static boolean containsTargetTypeParameter(List<Parameter> parameters) {
|
||||||
|
for ( Parameter param : parameters ) {
|
||||||
|
if ( param.isTargetType() ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.
|
* excluding generic type variables. When the implementor sees a need for this, this method can be overridden.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean matches(Type sourceType, Type targetType) {
|
public boolean matches(List<Type> sourceTypes, Type targetType) {
|
||||||
|
if ( sourceTypes.size() > 1 ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Type sourceType = sourceTypes.iterator().next();
|
||||||
|
|
||||||
if ( targetType.erasure().isAssignableTo( getReturnType().erasure() )
|
if ( targetType.erasure().isAssignableTo( getReturnType().erasure() )
|
||||||
&& sourceType.erasure().isAssignableTo( getParameter().getType().erasure() ) ) {
|
&& sourceType.erasure().isAssignableTo( getParameter().getType().erasure() ) ) {
|
||||||
return doTypeVarsMatch( sourceType, targetType );
|
return doTypeVarsMatch( sourceType, targetType );
|
||||||
|
@ -24,7 +24,9 @@ import java.util.List;
|
|||||||
|
|
||||||
import javax.lang.model.util.Types;
|
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.Type;
|
||||||
|
import org.mapstruct.ap.model.common.TypeFactory;
|
||||||
import org.mapstruct.ap.model.source.Method;
|
import org.mapstruct.ap.model.source.Method;
|
||||||
import org.mapstruct.ap.model.source.SourceMethod;
|
import org.mapstruct.ap.model.source.SourceMethod;
|
||||||
|
|
||||||
@ -37,10 +39,10 @@ public class MethodSelectors implements MethodSelector {
|
|||||||
|
|
||||||
private final List<MethodSelector> selectors;
|
private final List<MethodSelector> selectors;
|
||||||
|
|
||||||
public MethodSelectors(Types typeUtils) {
|
public MethodSelectors(Types typeUtils, TypeFactory typeFactory) {
|
||||||
selectors =
|
selectors =
|
||||||
Arrays.<MethodSelector>asList(
|
Arrays.<MethodSelector>asList(
|
||||||
new TypeSelector(),
|
new TypeSelector( typeFactory ),
|
||||||
new InheritanceSelector(),
|
new InheritanceSelector(),
|
||||||
new XmlElementDeclSelector( typeUtils )
|
new XmlElementDeclSelector( typeUtils )
|
||||||
);
|
);
|
||||||
@ -64,4 +66,26 @@ public class MethodSelectors implements MethodSelector {
|
|||||||
}
|
}
|
||||||
return candidates;
|
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<Type> getParameterTypes(TypeFactory typeFactory, List<Parameter> parameters, Type sourceType,
|
||||||
|
Type returnType) {
|
||||||
|
List<Type> result = new ArrayList<Type>( parameters.size() );
|
||||||
|
for ( Parameter param : parameters ) {
|
||||||
|
if ( param.isTargetType() ) {
|
||||||
|
result.add( typeFactory.classTypeOf( returnType ) );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result.add( sourceType );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.mapstruct.ap.model.common.Type;
|
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.Method;
|
||||||
import org.mapstruct.ap.model.source.MethodMatcher;
|
import org.mapstruct.ap.model.source.MethodMatcher;
|
||||||
import org.mapstruct.ap.model.source.SourceMethod;
|
import org.mapstruct.ap.model.source.SourceMethod;
|
||||||
@ -34,6 +35,12 @@ import org.mapstruct.ap.model.source.SourceMethod;
|
|||||||
*/
|
*/
|
||||||
public class TypeSelector implements MethodSelector {
|
public class TypeSelector implements MethodSelector {
|
||||||
|
|
||||||
|
private final TypeFactory typeFactory;
|
||||||
|
|
||||||
|
public TypeSelector(TypeFactory typeFactory) {
|
||||||
|
this.typeFactory = typeFactory;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T extends Method> List<T> getMatchingMethods(SourceMethod mappingMethod, List<T> methods,
|
public <T extends Method> List<T> getMatchingMethods(SourceMethod mappingMethod, List<T> methods,
|
||||||
Type parameterType, Type returnType,
|
Type parameterType, Type returnType,
|
||||||
@ -45,7 +52,9 @@ public class TypeSelector implements MethodSelector {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( method.matches( parameterType, returnType ) ) {
|
List<Type> parameterTypes =
|
||||||
|
MethodSelectors.getParameterTypes( typeFactory, method.getParameters(), parameterType, returnType );
|
||||||
|
if ( method.matches( parameterTypes, returnType ) ) {
|
||||||
result.add( method );
|
result.add( method );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ import org.mapstruct.MapMapping;
|
|||||||
import org.mapstruct.Mapper;
|
import org.mapstruct.Mapper;
|
||||||
import org.mapstruct.Mapping;
|
import org.mapstruct.Mapping;
|
||||||
import org.mapstruct.MappingTarget;
|
import org.mapstruct.MappingTarget;
|
||||||
|
import org.mapstruct.TargetType;
|
||||||
import org.mapstruct.Mappings;
|
import org.mapstruct.Mappings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -40,6 +41,7 @@ import org.mapstruct.Mappings;
|
|||||||
@GeneratePrism(value = Mappings.class, publicAccess = true),
|
@GeneratePrism(value = Mappings.class, publicAccess = true),
|
||||||
@GeneratePrism(value = IterableMapping.class, publicAccess = true),
|
@GeneratePrism(value = IterableMapping.class, publicAccess = true),
|
||||||
@GeneratePrism(value = MapMapping.class, publicAccess = true),
|
@GeneratePrism(value = MapMapping.class, publicAccess = true),
|
||||||
|
@GeneratePrism(value = TargetType.class, publicAccess = true),
|
||||||
@GeneratePrism(value = MappingTarget.class, publicAccess = true),
|
@GeneratePrism(value = MappingTarget.class, publicAccess = true),
|
||||||
|
|
||||||
// external types
|
// external types
|
||||||
|
@ -106,7 +106,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
|
|||||||
|
|
||||||
this.builtInMethods = new BuiltInMappingMethods( typeFactory );
|
this.builtInMethods = new BuiltInMappingMethods( typeFactory );
|
||||||
this.virtualMethods = new HashSet<VirtualMappingMethod>();
|
this.virtualMethods = new HashSet<VirtualMappingMethod>();
|
||||||
this.methodSelectors = new MethodSelectors( typeUtils );
|
this.methodSelectors = new MethodSelectors( typeUtils, typeFactory );
|
||||||
|
|
||||||
return getMapper( mapperTypeElement, sourceModel );
|
return getMapper( mapperTypeElement, sourceModel );
|
||||||
}
|
}
|
||||||
@ -257,18 +257,21 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
|
|||||||
MethodReference result = null;
|
MethodReference result = null;
|
||||||
for ( SourceMethod method : methods ) {
|
for ( SourceMethod method : methods ) {
|
||||||
if ( !method.requiresImplementation() && !method.isIterableMapping() && !method.isMapMapping()
|
if ( !method.requiresImplementation() && !method.isIterableMapping() && !method.isMapMapping()
|
||||||
&& method.getMappings().isEmpty() && method.getParameters().isEmpty() ) {
|
&& method.getSourceParameters().size() == 0 ) {
|
||||||
if ( method.getReturnType().equals( returnType ) ) {
|
|
||||||
if ( result == null ) {
|
|
||||||
MapperReference mapperReference = null;
|
|
||||||
for ( MapperReference ref : mapperReferences ) {
|
|
||||||
if ( ref.getMapperType().equals( method.getDeclaringMapper() ) ) {
|
|
||||||
mapperReference = ref;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result = new MethodReference( method, mapperReference );
|
List<Type> 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 {
|
else {
|
||||||
messager.printMessage(
|
messager.printMessage(
|
||||||
@ -1012,7 +1015,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
|
|||||||
);
|
);
|
||||||
|
|
||||||
if ( matchingSourceMethod != null ) {
|
if ( matchingSourceMethod != null ) {
|
||||||
return getMappingMethodReference( matchingSourceMethod, mapperReferences );
|
return getMappingMethodReference( matchingSourceMethod, mapperReferences, targetType );
|
||||||
}
|
}
|
||||||
|
|
||||||
// then a matching built-in method
|
// then a matching built-in method
|
||||||
@ -1111,14 +1114,14 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
|
|||||||
private <T extends Method> T getBestMatch(SourceMethod mappingMethod,
|
private <T extends Method> T getBestMatch(SourceMethod mappingMethod,
|
||||||
String mappedElement,
|
String mappedElement,
|
||||||
List<T> methods,
|
List<T> methods,
|
||||||
Type parameterType,
|
Type sourceType,
|
||||||
Type returnType,
|
Type returnType,
|
||||||
String targetPropertyName) {
|
String targetPropertyName) {
|
||||||
|
|
||||||
List<T> candidates = methodSelectors.getMatchingMethods(
|
List<T> candidates = methodSelectors.getMatchingMethods(
|
||||||
mappingMethod,
|
mappingMethod,
|
||||||
methods,
|
methods,
|
||||||
parameterType,
|
sourceType,
|
||||||
returnType,
|
returnType,
|
||||||
targetPropertyName
|
targetPropertyName
|
||||||
);
|
);
|
||||||
@ -1130,7 +1133,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
|
|||||||
Kind.ERROR,
|
Kind.ERROR,
|
||||||
String.format(
|
String.format(
|
||||||
"Ambiguous mapping methods found for mapping " + mappedElement + " from %s to %s: %s.",
|
"Ambiguous mapping methods found for mapping " + mappedElement + " from %s to %s: %s.",
|
||||||
parameterType,
|
sourceType,
|
||||||
returnType,
|
returnType,
|
||||||
Strings.join( candidates, ", " )
|
Strings.join( candidates, ", " )
|
||||||
),
|
),
|
||||||
@ -1145,15 +1148,23 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MethodReference getMappingMethodReference(SourceMethod method, List<MapperReference> mapperReferences) {
|
private MethodReference getMappingMethodReference(SourceMethod method, List<MapperReference> mapperReferences,
|
||||||
MapperReference mapperReference = null;
|
Type targetType) {
|
||||||
|
MapperReference mapperReference = findMapperReference( mapperReferences, method );
|
||||||
|
|
||||||
|
return new MethodReference(
|
||||||
|
method,
|
||||||
|
mapperReference,
|
||||||
|
SourceMethod.containsTargetTypeParameter( method.getParameters() ) ? targetType : null );
|
||||||
|
}
|
||||||
|
|
||||||
|
private MapperReference findMapperReference(List<MapperReference> mapperReferences, SourceMethod method) {
|
||||||
for ( MapperReference ref : mapperReferences ) {
|
for ( MapperReference ref : mapperReferences ) {
|
||||||
if ( ref.getMapperType().equals( method.getDeclaringMapper() ) ) {
|
if ( ref.getMapperType().equals( method.getDeclaringMapper() ) ) {
|
||||||
mapperReference = ref;
|
return ref;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new MethodReference( method, mapperReference );
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MethodReference getMappingMethodReference(BuiltInMethod method, Type returnType, String dateFormat) {
|
private MethodReference getMappingMethodReference(BuiltInMethod method, Type returnType, String dateFormat) {
|
||||||
|
@ -18,10 +18,13 @@
|
|||||||
*/
|
*/
|
||||||
package org.mapstruct.ap.processor;
|
package org.mapstruct.ap.processor;
|
||||||
|
|
||||||
|
import static javax.lang.model.util.ElementFilter.methodsIn;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.annotation.processing.Messager;
|
import javax.annotation.processing.Messager;
|
||||||
import javax.lang.model.element.ExecutableElement;
|
import javax.lang.model.element.ExecutableElement;
|
||||||
import javax.lang.model.element.Modifier;
|
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.prism.MappingsPrism;
|
||||||
import org.mapstruct.ap.util.AnnotationProcessingException;
|
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
|
* 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
|
* representing all the mapping methods of the given bean mapper type as well as
|
||||||
@ -125,6 +126,7 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
|||||||
|
|
||||||
//add method with property mappings if an implementation needs to be generated
|
//add method with property mappings if an implementation needs to be generated
|
||||||
boolean methodRequiresImplementation = method.getModifiers().contains( Modifier.ABSTRACT );
|
boolean methodRequiresImplementation = method.getModifiers().contains( Modifier.ABSTRACT );
|
||||||
|
boolean containsTargetTypeParameter = SourceMethod.containsTargetTypeParameter( parameters );
|
||||||
|
|
||||||
if ( mapperRequiresImplementation && methodRequiresImplementation ) {
|
if ( mapperRequiresImplementation && methodRequiresImplementation ) {
|
||||||
List<Parameter> sourceParameters = extractSourceParameters( parameters );
|
List<Parameter> sourceParameters = extractSourceParameters( parameters );
|
||||||
@ -132,7 +134,13 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
|||||||
Type resultType = selectResultType( returnType, targetParameter );
|
Type resultType = selectResultType( returnType, targetParameter );
|
||||||
|
|
||||||
boolean isValid =
|
boolean isValid =
|
||||||
checkParameterAndReturnType( method, sourceParameters, targetParameter, resultType, returnType );
|
checkParameterAndReturnType(
|
||||||
|
method,
|
||||||
|
sourceParameters,
|
||||||
|
targetParameter,
|
||||||
|
resultType,
|
||||||
|
returnType,
|
||||||
|
containsTargetTypeParameter );
|
||||||
|
|
||||||
if ( isValid ) {
|
if ( isValid ) {
|
||||||
return
|
return
|
||||||
@ -151,7 +159,7 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
//otherwise add reference to existing mapper method
|
//otherwise add reference to existing mapper method
|
||||||
else if ( parameters.size() == 1 ) {
|
else if ( isValidReferencedMethod( parameters ) || isValidFactoryMethod( parameters ) ) {
|
||||||
return
|
return
|
||||||
SourceMethod.forReferencedMethod(
|
SourceMethod.forReferencedMethod(
|
||||||
mapperRequiresImplementation ? null : typeFactory.getType( element ),
|
mapperRequiresImplementation ? null : typeFactory.getType( element ),
|
||||||
@ -161,21 +169,41 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
|||||||
typeUtils
|
typeUtils
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
//create factory method
|
|
||||||
else if ( parameters.isEmpty() ) {
|
|
||||||
return
|
|
||||||
SourceMethod.forFactoryMethod(
|
|
||||||
mapperRequiresImplementation ? null : typeFactory.getType( element ),
|
|
||||||
method,
|
|
||||||
returnType,
|
|
||||||
typeUtils
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isValidReferencedMethod(List<Parameter> parameters) {
|
||||||
|
return isValidReferencedOrFactoryMethod( 1, parameters );
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isValidFactoryMethod(List<Parameter> parameters) {
|
||||||
|
return isValidReferencedOrFactoryMethod( 0, parameters );
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isValidReferencedOrFactoryMethod(int sourceParamCount, List<Parameter> 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<Parameter> parameters) {
|
private Parameter extractTargetParameter(List<Parameter> parameters) {
|
||||||
for ( Parameter param : parameters ) {
|
for ( Parameter param : parameters ) {
|
||||||
if ( param.isMappingTarget() ) {
|
if ( param.isMappingTarget() ) {
|
||||||
@ -207,7 +235,8 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkParameterAndReturnType(ExecutableElement method, List<Parameter> sourceParameters,
|
private boolean checkParameterAndReturnType(ExecutableElement method, List<Parameter> sourceParameters,
|
||||||
Parameter targetParameter, Type resultType, Type returnType) {
|
Parameter targetParameter, Type resultType, Type returnType,
|
||||||
|
boolean containsTargetTypeParameter) {
|
||||||
if ( sourceParameters.isEmpty() ) {
|
if ( sourceParameters.isEmpty() ) {
|
||||||
messager.printMessage( Kind.ERROR, "Can't generate mapping method with no input arguments.", method );
|
messager.printMessage( Kind.ERROR, "Can't generate mapping method with no input arguments.", method );
|
||||||
return false;
|
return false;
|
||||||
@ -248,6 +277,14 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( containsTargetTypeParameter ) {
|
||||||
|
messager.printMessage(
|
||||||
|
Kind.ERROR,
|
||||||
|
"Can't generate mapping method that has a parameter annotated with @TargetType.",
|
||||||
|
method );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if ( !parameterType.isIterableType() && resultType.isIterableType() ) {
|
if ( !parameterType.isIterableType() && resultType.isIterableType() ) {
|
||||||
messager.printMessage(
|
messager.printMessage(
|
||||||
Kind.ERROR,
|
Kind.ERROR,
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
return<#if returnType.name != "void"> null</#if>;
|
return<#if returnType.name != "void"> 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)>
|
<#if (sourceParameters?size > 1)>
|
||||||
<#list sourceParameters as sourceParam>
|
<#list sourceParameters as sourceParam>
|
||||||
if ( ${sourceParam.name} != null ) {
|
if ( ${sourceParam.name} != null ) {
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
|
|
||||||
for ( <@includeModel object=sourceParameter.type.typeParameters[0]/> ${loopVariableName} : ${sourceParameter.name} ) {
|
for ( <@includeModel object=sourceParameter.type.typeParameters[0]/> ${loopVariableName} : ${sourceParameter.name} ) {
|
||||||
<#if elementMappingMethod??>
|
<#if elementMappingMethod??>
|
||||||
${resultName}.add( <@includeModel object=elementMappingMethod input="${loopVariableName}"/> );
|
${resultName}.add( <@includeModel object=elementMappingMethod input="${loopVariableName}" targetType="${resultType.typeParameters[0].name}"/> );
|
||||||
<#elseif conversion??>
|
<#elseif conversion??>
|
||||||
<#if (conversion.exceptionTypes?size == 0) >
|
<#if (conversion.exceptionTypes?size == 0) >
|
||||||
${resultName}.add( <@includeModel object=conversion/> );
|
${resultName}.add( <@includeModel object=conversion/> );
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
|
|
||||||
<#-- key -->
|
<#-- key -->
|
||||||
<#if keyMappingMethod??>
|
<#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??>
|
<#elseif keyConversion??>
|
||||||
<#if (keyConversion.exceptionTypes?size == 0) >
|
<#if (keyConversion.exceptionTypes?size == 0) >
|
||||||
<@includeModel object=resultType.typeParameters[0]/> ${keyVariableName} = <@includeModel object=keyConversion/>;
|
<@includeModel object=resultType.typeParameters[0]/> ${keyVariableName} = <@includeModel object=keyConversion/>;
|
||||||
@ -55,7 +55,7 @@
|
|||||||
</#if>
|
</#if>
|
||||||
<#-- value -->
|
<#-- value -->
|
||||||
<#if valueMappingMethod??>
|
<#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??>
|
<#elseif valueConversion??>
|
||||||
<#if (valueConversion.exceptionTypes?size == 0) >
|
<#if (valueConversion.exceptionTypes?size == 0) >
|
||||||
<@includeModel object=resultType.typeParameters[1]/> ${valueVariableName} = <@includeModel object=valueConversion/>;
|
<@includeModel object=resultType.typeParameters[1]/> ${valueVariableName} = <@includeModel object=valueConversion/>;
|
||||||
|
@ -18,4 +18,8 @@
|
|||||||
limitations under the License.
|
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>
|
<#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>
|
@ -21,10 +21,10 @@
|
|||||||
<#-- a) invoke mapping method -->
|
<#-- a) invoke mapping method -->
|
||||||
<#if mappingMethod??>
|
<#if mappingMethod??>
|
||||||
<#if targetAccessorSetter>
|
<#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>
|
<#elseif targetType.collectionType>
|
||||||
if ( ${sourceBeanName}.${sourceAccessorName}() != null && ${ext.targetBeanName}.${targetAccessorName}() != null ) {
|
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>
|
</#if>
|
||||||
<#-- b) simple conversion -->
|
<#-- b) simple conversion -->
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
package org.mapstruct.ap.test.erroneous.typemismatch;
|
package org.mapstruct.ap.test.erroneous.typemismatch;
|
||||||
|
|
||||||
import org.mapstruct.Mapper;
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.TargetType;
|
||||||
|
|
||||||
@Mapper
|
@Mapper
|
||||||
public interface ErroneousMapper {
|
public interface ErroneousMapper {
|
||||||
@ -30,4 +31,6 @@ public interface ErroneousMapper {
|
|||||||
long sourceToLong(Source source);
|
long sourceToLong(Source source);
|
||||||
|
|
||||||
Source longToSource(long id);
|
Source longToSource(long id);
|
||||||
|
|
||||||
|
Target sourceToTargetWithMappingTargetType(Source source, @TargetType Class<?> clazz);
|
||||||
}
|
}
|
||||||
|
@ -43,20 +43,25 @@ public class ErroneousMappingsTest extends MapperTestBase {
|
|||||||
diagnostics = {
|
diagnostics = {
|
||||||
@Diagnostic(type = ErroneousMapper.class,
|
@Diagnostic(type = ErroneousMapper.class,
|
||||||
kind = Kind.ERROR,
|
kind = Kind.ERROR,
|
||||||
line = 26,
|
line = 27,
|
||||||
messageRegExp = "Can't map property \"boolean foo\" to \"int foo\"\\."),
|
messageRegExp = "Can't map property \"boolean foo\" to \"int foo\"\\."),
|
||||||
@Diagnostic(type = ErroneousMapper.class,
|
@Diagnostic(type = ErroneousMapper.class,
|
||||||
kind = Kind.ERROR,
|
kind = Kind.ERROR,
|
||||||
line = 28,
|
line = 29,
|
||||||
messageRegExp = "Can't map property \"int foo\" to \"boolean foo\"\\."),
|
messageRegExp = "Can't map property \"int foo\" to \"boolean foo\"\\."),
|
||||||
@Diagnostic(type = ErroneousMapper.class,
|
@Diagnostic(type = ErroneousMapper.class,
|
||||||
kind = Kind.ERROR,
|
kind = Kind.ERROR,
|
||||||
line = 30,
|
line = 31,
|
||||||
messageRegExp = "Can't generate mapping method with primitive return type\\."),
|
messageRegExp = "Can't generate mapping method with primitive return type\\."),
|
||||||
@Diagnostic(type = ErroneousMapper.class,
|
@Diagnostic(type = ErroneousMapper.class,
|
||||||
kind = Kind.ERROR,
|
kind = Kind.ERROR,
|
||||||
line = 32,
|
line = 33,
|
||||||
messageRegExp = "Can't generate mapping method with primitive parameter type\\.")
|
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() {
|
public void shouldFailToGenerateMappings() {
|
||||||
|
@ -21,7 +21,7 @@ package org.mapstruct.ap.test.factories;
|
|||||||
/**
|
/**
|
||||||
* @author Sjaak Derksen
|
* @author Sjaak Derksen
|
||||||
*/
|
*/
|
||||||
public class Bar1 {
|
public class Bar1 implements FactoryCreatable {
|
||||||
private String prop;
|
private String prop;
|
||||||
|
|
||||||
private final String someTypeProp;
|
private final String someTypeProp;
|
||||||
|
@ -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);
|
||||||
|
}
|
@ -18,6 +18,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.mapstruct.ap.test.factories;
|
package org.mapstruct.ap.test.factories;
|
||||||
|
|
||||||
|
import static org.fest.assertions.Assertions.assertThat;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -29,8 +31,6 @@ import org.mapstruct.ap.testutil.MapperTestBase;
|
|||||||
import org.mapstruct.ap.testutil.WithClasses;
|
import org.mapstruct.ap.testutil.WithClasses;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
import static org.fest.assertions.Assertions.assertThat;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Sjaak Derksen
|
* @author Sjaak Derksen
|
||||||
*/
|
*/
|
||||||
@ -38,7 +38,7 @@ import static org.fest.assertions.Assertions.assertThat;
|
|||||||
@WithClasses({
|
@WithClasses({
|
||||||
Bar1.class, Foo1.class, Bar2.class, Foo2.class, Bar3.class, Foo3.class, BarFactory.class,
|
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,
|
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 {
|
public class FactoryTest extends MapperTestBase {
|
||||||
@Test
|
@Test
|
||||||
@ -87,4 +87,19 @@ public class FactoryTest extends MapperTestBase {
|
|||||||
source.setPropMap( fooMap );
|
source.setPropMap( fooMap );
|
||||||
return source;
|
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" );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 extends FactoryCreatable> T createNew(@TargetType Class<T> clazz) {
|
||||||
|
if ( clazz == Bar1.class ) {
|
||||||
|
return (T) new Bar1( "created by GenericFactory" );
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
@ -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 {
|
||||||
|
|
||||||
|
}
|
@ -18,6 +18,10 @@
|
|||||||
*/
|
*/
|
||||||
package org.mapstruct.ap.test.references;
|
package org.mapstruct.ap.test.references;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.mapstruct.TargetType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Andreas Gudian
|
* @author Andreas Gudian
|
||||||
*
|
*
|
||||||
@ -26,4 +30,24 @@ public class ReferencedCustomMapper {
|
|||||||
public long incrementingIntToLong(int source) {
|
public long incrementingIntToLong(int source) {
|
||||||
return source + 1;
|
return source + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings( "unchecked" )
|
||||||
|
public <T extends BaseType> T convert(String string, @TargetType Class<T> 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 extends Map<?, ?>> T unused(String string, @TargetType Class<T> clazz) {
|
||||||
|
throw new RuntimeException( "should never be called" );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,12 @@
|
|||||||
package org.mapstruct.ap.test.references;
|
package org.mapstruct.ap.test.references;
|
||||||
|
|
||||||
import static org.fest.assertions.Assertions.assertThat;
|
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.IssueKey;
|
||||||
import org.mapstruct.ap.testutil.MapperTestBase;
|
import org.mapstruct.ap.testutil.MapperTestBase;
|
||||||
@ -31,7 +37,7 @@ import org.testng.annotations.Test;
|
|||||||
*/
|
*/
|
||||||
@IssueKey( "82" )
|
@IssueKey( "82" )
|
||||||
@WithClasses( { Bar.class, Foo.class, FooMapper.class, ReferencedCustomMapper.class, Source.class,
|
@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 {
|
public class ReferencedMapperTest extends MapperTestBase {
|
||||||
@Test
|
@Test
|
||||||
public void referencedMappersAreInstatiatedCorrectly() {
|
public void referencedMappersAreInstatiatedCorrectly() {
|
||||||
@ -41,6 +47,7 @@ public class ReferencedMapperTest extends MapperTestBase {
|
|||||||
assertThat( target.getProp1() ).isEqualTo( 43 );
|
assertThat( target.getProp1() ).isEqualTo( 43 );
|
||||||
assertThat( target.getProp2() ).isNotNull();
|
assertThat( target.getProp2() ).isNotNull();
|
||||||
assertThat( target.getProp2().getProp1() ).isEqualTo( "foo" );
|
assertThat( target.getProp2().getProp1() ).isEqualTo( "foo" );
|
||||||
|
assertThat( target.getProp3().getValue() ).isEqualTo( "prop3" );
|
||||||
}
|
}
|
||||||
|
|
||||||
private Source createSource() {
|
private Source createSource() {
|
||||||
@ -50,7 +57,46 @@ public class ReferencedMapperTest extends MapperTestBase {
|
|||||||
Foo prop2 = new Foo();
|
Foo prop2 = new Foo();
|
||||||
prop2.setProp1( "foo" );
|
prop2.setProp1( "foo" );
|
||||||
source.setProp2( prop2 );
|
source.setProp2( prop2 );
|
||||||
|
source.setProp3( "prop3" );
|
||||||
|
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@IssueKey( "136" )
|
||||||
|
public void shouldUseGenericFactoryForIterable() {
|
||||||
|
List<SomeType> result = SourceTargetMapper.INSTANCE.fromStringList( Arrays.asList( "foo1", "foo2" ) );
|
||||||
|
|
||||||
|
assertThat( result ).onProperty( "value" ).containsExactly( "foo1", "foo2" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@IssueKey( "136" )
|
||||||
|
public void shouldUseGenericFactoryForMap() {
|
||||||
|
Map<String, String> source = new HashMap<String, String>();
|
||||||
|
source.put( "foo1", "bar1" );
|
||||||
|
source.put( "foo2", "bar2" );
|
||||||
|
Map<SomeType, SomeOtherType> 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 );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -25,6 +25,7 @@ package org.mapstruct.ap.test.references;
|
|||||||
public class Source {
|
public class Source {
|
||||||
private int prop1;
|
private int prop1;
|
||||||
private Foo prop2;
|
private Foo prop2;
|
||||||
|
private String prop3;
|
||||||
|
|
||||||
public int getProp1() {
|
public int getProp1() {
|
||||||
return prop1;
|
return prop1;
|
||||||
@ -41,4 +42,12 @@ public class Source {
|
|||||||
public void setProp2(Foo prop2) {
|
public void setProp2(Foo prop2) {
|
||||||
this.prop2 = prop2;
|
this.prop2 = prop2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getProp3() {
|
||||||
|
return prop3;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProp3(String prop3) {
|
||||||
|
this.prop3 = prop3;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.mapstruct.ap.test.references;
|
package org.mapstruct.ap.test.references;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.mapstruct.Mapper;
|
import org.mapstruct.Mapper;
|
||||||
import org.mapstruct.factory.Mappers;
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
@ -30,4 +33,8 @@ public interface SourceTargetMapper {
|
|||||||
SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class );
|
SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class );
|
||||||
|
|
||||||
Target sourceToTarget(Source source);
|
Target sourceToTarget(Source source);
|
||||||
|
|
||||||
|
List<SomeType> fromStringList(List<String> stringList);
|
||||||
|
|
||||||
|
Map<SomeType, SomeOtherType> fromStringMap(Map<String, String> stringStringMap);
|
||||||
}
|
}
|
||||||
|
@ -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> T convert(@TargetType Class<T> 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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.mapstruct.ap.test.references;
|
package org.mapstruct.ap.test.references;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Andreas Gudian
|
* @author Andreas Gudian
|
||||||
*
|
*
|
||||||
@ -25,6 +26,7 @@ package org.mapstruct.ap.test.references;
|
|||||||
public class Target {
|
public class Target {
|
||||||
private long prop1;
|
private long prop1;
|
||||||
private Bar prop2;
|
private Bar prop2;
|
||||||
|
private SomeType prop3;
|
||||||
|
|
||||||
public long getProp1() {
|
public long getProp1() {
|
||||||
return prop1;
|
return prop1;
|
||||||
@ -41,4 +43,12 @@ public class Target {
|
|||||||
public void setProp2(Bar prop2) {
|
public void setProp2(Bar prop2) {
|
||||||
this.prop2 = prop2;
|
this.prop2 = prop2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SomeType getProp3() {
|
||||||
|
return prop3;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProp3(SomeType prop3) {
|
||||||
|
this.prop3 = prop3;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user