#19 Map into existing instances.

- Either defined explicitly with @MappingTarget, or implicitly as last parameter in a void method.
This commit is contained in:
Andreas Gudian 2013-07-16 23:09:57 +02:00
parent ba90702392
commit 466a640ced
24 changed files with 626 additions and 158 deletions

View File

@ -0,0 +1,36 @@
/**
* Copyright 2012-2013 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
/**
* Declares a parameter of a mapping method to be the target of the mapping.
* <p>
* 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
*/
@Target( ElementType.PARAMETER )
public @interface MappingTarget {
}

View File

@ -43,6 +43,7 @@ import org.mapstruct.IterableMapping;
import org.mapstruct.MapMapping; 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.Mappings; import org.mapstruct.Mappings;
import org.mapstruct.ap.model.Options; import org.mapstruct.ap.model.Options;
import org.mapstruct.ap.model.ReportingPolicy; import org.mapstruct.ap.model.ReportingPolicy;
@ -76,7 +77,8 @@ import org.mapstruct.ap.processor.ModelElementProcessor.ProcessorContext;
@GeneratePrism(value = Mapping.class, publicAccess = true), @GeneratePrism(value = Mapping.class, publicAccess = true),
@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 = MappingTarget.class, publicAccess = true)
}) })
@SupportedOptions({ @SupportedOptions({
MappingProcessor.SUPPRESS_GENERATOR_TIMESTAMP, MappingProcessor.SUPPRESS_GENERATOR_TIMESTAMP,

View File

@ -21,6 +21,8 @@ package org.mapstruct.ap.model;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import org.mapstruct.ap.model.source.Parameter;
/** /**
* A {@link MappingMethod} implemented by a {@link Mapper} class which maps one * A {@link MappingMethod} implemented by a {@link Mapper} class which maps one
* bean type to another, optionally configured by one or more * bean type to another, optionally configured by one or more
@ -32,9 +34,10 @@ public class BeanMappingMethod extends MappingMethod {
private final List<PropertyMapping> propertyMappings; private final List<PropertyMapping> propertyMappings;
public BeanMappingMethod(String name, String parameterName, Type sourceType, Type targetType, public BeanMappingMethod(String name, List<Parameter> parameters, List<Parameter> sourceParameters,
Type resultType, String resultName, Type returnType,
List<PropertyMapping> propertyMappings) { List<PropertyMapping> propertyMappings) {
super( name, parameterName, sourceType, targetType ); super( name, parameters, sourceParameters, resultType, resultName, returnType );
this.propertyMappings = propertyMappings; this.propertyMappings = propertyMappings;
} }

View File

@ -18,8 +18,11 @@
*/ */
package org.mapstruct.ap.model; package org.mapstruct.ap.model;
import java.util.List;
import java.util.Set; import java.util.Set;
import org.mapstruct.ap.model.source.Parameter;
/** /**
* A {@link MappingMethod} implemented by a {@link Mapper} class which maps one iterable type to another. The collection * A {@link MappingMethod} implemented by a {@link Mapper} class which maps one iterable type to another. The collection
* elements are mapped either by a {@link TypeConversion} or another mapping method. * elements are mapped either by a {@link TypeConversion} or another mapping method.
@ -31,9 +34,10 @@ public class IterableMappingMethod extends MappingMethod {
private final MappingMethodReference elementMappingMethod; private final MappingMethodReference elementMappingMethod;
private final TypeConversion conversion; private final TypeConversion conversion;
public IterableMappingMethod(String name, String parameterName, Type sourceType, Type targetType, public IterableMappingMethod(String name, List<Parameter> parameters, List<Parameter> sourceParameters,
Type resultType, String resultName, Type returnType,
MappingMethodReference elementMappingMethod, TypeConversion conversion) { MappingMethodReference elementMappingMethod, TypeConversion conversion) {
super( name, parameterName, sourceType, targetType ); super( name, parameters, sourceParameters, resultType, resultName, returnType );
this.elementMappingMethod = elementMappingMethod; this.elementMappingMethod = elementMappingMethod;
this.conversion = conversion; this.conversion = conversion;
} }

View File

@ -18,8 +18,11 @@
*/ */
package org.mapstruct.ap.model; package org.mapstruct.ap.model;
import java.util.List;
import java.util.Set; import java.util.Set;
import org.mapstruct.ap.model.source.Parameter;
/** /**
* A {@link MappingMethod} implemented by a {@link Mapper} class which maps one {@code Map} type to another. Keys and * A {@link MappingMethod} implemented by a {@link Mapper} class which maps one {@code Map} type to another. Keys and
* values are mapped either by a {@link TypeConversion} or another mapping method if required. * values are mapped either by a {@link TypeConversion} or another mapping method if required.
@ -33,10 +36,11 @@ public class MapMappingMethod extends MappingMethod {
private final MappingMethodReference valueMappingMethod; private final MappingMethodReference valueMappingMethod;
private final TypeConversion valueConversion; private final TypeConversion valueConversion;
public MapMappingMethod(String name, String parameterName, Type sourceType, Type targetType, public MapMappingMethod(String name, List<Parameter> parameters, List<Parameter> sourceParameters, Type resultType,
String resultName, Type returnType,
MappingMethodReference keyMappingMethod, TypeConversion keyConversion, MappingMethodReference keyMappingMethod, TypeConversion keyConversion,
MappingMethodReference valueMappingMethod, TypeConversion valueConversion) { MappingMethodReference valueMappingMethod, TypeConversion valueConversion) {
super( name, parameterName, sourceType, targetType ); super( name, parameters, sourceParameters, resultType, resultName, returnType );
this.keyMappingMethod = keyMappingMethod; this.keyMappingMethod = keyMappingMethod;
this.keyConversion = keyConversion; this.keyConversion = keyConversion;
@ -68,8 +72,4 @@ public class MapMappingMethod extends MappingMethod {
return types; return types;
} }
public String getReturnValueName() {
return "map";
}
} }

View File

@ -19,8 +19,11 @@
package org.mapstruct.ap.model; package org.mapstruct.ap.model;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
import org.mapstruct.ap.model.source.Parameter;
/** /**
* A method implemented or referenced by a {@link Mapper} class. * A method implemented or referenced by a {@link Mapper} class.
* *
@ -29,38 +32,62 @@ import java.util.Set;
public abstract class MappingMethod extends AbstractModelElement { public abstract class MappingMethod extends AbstractModelElement {
private final String name; private final String name;
private final String parameterName; private final List<Parameter> parameters;
private final Type sourceType; private final List<Parameter> sourceParameters;
private final Type targetType; private final Type resultType;
private final String resultName;
private final Type returnType;
private final boolean existingInstanceMapping;
protected MappingMethod(String name, String parameterName, Type sourceType, Type targetType) { protected MappingMethod(String name, List<Parameter> parameters, List<Parameter> sourceParameters, Type resultType,
String resultName, Type returnType) {
this.name = name; this.name = name;
this.parameterName = parameterName; this.parameters = parameters;
this.sourceType = sourceType; this.sourceParameters = sourceParameters;
this.targetType = targetType; this.resultType = resultType;
this.resultName = resultName;
this.returnType = returnType;
this.existingInstanceMapping =
( null != parameters && null != sourceParameters && parameters.size() > sourceParameters.size() );
} }
public String getName() { public String getName() {
return name; return name;
} }
public String getParameterName() { public List<Parameter> getParameters() {
return parameterName; return parameters;
} }
public Type getSourceType() { public List<Parameter> getSourceParameters() {
return sourceType; return sourceParameters;
} }
public Type getTargetType() { public Type getResultType() {
return targetType; return resultType;
}
public String getResultName() {
return resultName;
}
public Type getReturnType() {
return returnType;
}
public boolean isExistingInstanceMapping() {
return existingInstanceMapping;
} }
@Override @Override
public Set<Type> getImportTypes() { public Set<Type> getImportTypes() {
Set<Type> types = new HashSet<Type>(); Set<Type> types = new HashSet<Type>();
types.add( getSourceType() );
types.add( getTargetType() ); for ( Parameter param : getParameters() ) {
types.add( param.getType() );
}
types.add( getReturnType() );
return types; return types;
} }
@ -69,7 +96,7 @@ public abstract class MappingMethod extends AbstractModelElement {
public String toString() { public String toString() {
return "MappingMethod {" + return "MappingMethod {" +
"\n name='" + name + "\'," + "\n name='" + name + "\'," +
"\n parameterName='" + parameterName + "\'," + "\n parameters='" + parameters + "\'," +
"\n}"; "\n}";
} }
} }

View File

@ -30,9 +30,8 @@ public class MappingMethodReference extends MappingMethod {
private final Type declaringMapper; private final Type declaringMapper;
public MappingMethodReference(Type declaringMapper, String name, String parameterName, Type sourceType, public MappingMethodReference(Type declaringMapper, String name) {
Type targetType) { super( name, null, null, null, null, null );
super( name, parameterName, sourceType, targetType );
this.declaringMapper = declaringMapper; this.declaringMapper = declaringMapper;
} }
@ -41,10 +40,6 @@ public class MappingMethodReference extends MappingMethod {
} }
public Set<Type> getReferencedTypes() { public Set<Type> getReferencedTypes() {
Set<Type> types = new HashSet<Type>(); return new HashSet<Type>();
types.add( getSourceType() );
types.add( getTargetType() );
return types;
} }
} }

View File

@ -22,7 +22,7 @@ package org.mapstruct.ap.model;
* The options passed to the code generator. * The options passed to the code generator.
* *
* @author Andreas Gudian * @author Andreas Gudian
* @autor Gunnar Morling * @author Gunnar Morling
*/ */
public class Options { public class Options {
private final boolean suppressGeneratorTimestamp; private final boolean suppressGeneratorTimestamp;

View File

@ -38,6 +38,10 @@ import org.mapstruct.ap.util.Strings;
* @author Gunnar Morling * @author Gunnar Morling
*/ */
public class Type extends AbstractModelElement implements Comparable<Type> { public class Type extends AbstractModelElement implements Comparable<Type> {
/**
* Type representing {@code void}
*/
public static final Type VOID = new Type( "void" );
private static final Set<String> PRIMITIVE_TYPE_NAMES = new HashSet<String>( private static final Set<String> PRIMITIVE_TYPE_NAMES = new HashSet<String>(
Arrays.asList( "boolean", "char", "byte", "short", "int", "long", "float", "double" ) Arrays.asList( "boolean", "char", "byte", "short", "int", "long", "float", "double" )
@ -61,6 +65,7 @@ public class Type extends AbstractModelElement implements Comparable<Type> {
DEFAULT_MAP_IMPLEMENTATION_TYPES.put( Map.class.getName(), forClass( HashMap.class ) ); DEFAULT_MAP_IMPLEMENTATION_TYPES.put( Map.class.getName(), forClass( HashMap.class ) );
} }
private final String canonicalName;
private final String packageName; private final String packageName;
private final String name; private final String name;
private final List<Type> typeParameters; private final List<Type> typeParameters;
@ -77,6 +82,7 @@ public class Type extends AbstractModelElement implements Comparable<Type> {
if ( pakkage != null ) { if ( pakkage != null ) {
return new Type( return new Type(
clazz.getCanonicalName(),
pakkage.getName(), pakkage.getName(),
clazz.getSimpleName(), clazz.getSimpleName(),
clazz.isEnum(), clazz.isEnum(),
@ -92,15 +98,16 @@ public class Type extends AbstractModelElement implements Comparable<Type> {
} }
public Type(String name) { public Type(String name) {
this( null, name, false, false, false, false, Collections.<Type>emptyList() ); this( name, null, name, false, false, false, false, Collections.<Type>emptyList() );
} }
public Type(String packageName, String name) { public Type(String packageName, String name) {
this( packageName, name, false, false, false, false, Collections.<Type>emptyList() ); this( packageName + "." + name, packageName, name, false, false, false, false, Collections.<Type>emptyList() );
} }
public Type(String packageName, String name, boolean isEnumType, boolean isCollectionType, public Type(String canonicalName, String packageName, String name, boolean isEnumType, boolean isCollectionType,
boolean isIterableType, boolean isMapType, List<Type> typeParameters) { boolean isIterableType, boolean isMapType, List<Type> typeParameters) {
this.canonicalName = canonicalName;
this.packageName = packageName; this.packageName = packageName;
this.name = name; this.name = name;
this.isEnumType = isEnumType; this.isEnumType = isEnumType;
@ -126,6 +133,7 @@ public class Type extends AbstractModelElement implements Comparable<Type> {
if ( isMapType ) { if ( isMapType ) {
Type mapType = DEFAULT_MAP_IMPLEMENTATION_TYPES.get( packageName + "." + name ); Type mapType = DEFAULT_MAP_IMPLEMENTATION_TYPES.get( packageName + "." + name );
mapImplementationType = mapType != null ? new Type( mapImplementationType = mapType != null ? new Type(
mapType.getPackageName() + "." + mapType.getName(),
mapType.getPackageName(), mapType.getPackageName(),
mapType.getName(), mapType.getName(),
mapType.isEnumType(), mapType.isEnumType(),
@ -140,6 +148,10 @@ public class Type extends AbstractModelElement implements Comparable<Type> {
} }
} }
public String getCanonicalName() {
return canonicalName;
}
public String getPackageName() { public String getPackageName() {
return packageName; return packageName;
} }

View File

@ -18,7 +18,10 @@
*/ */
package org.mapstruct.ap.model.source; package org.mapstruct.ap.model.source;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map; import java.util.Map;
import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.ExecutableElement;
@ -34,23 +37,28 @@ public class Method {
private final Type declaringMapper; private final Type declaringMapper;
private final ExecutableElement executable; private final ExecutableElement executable;
private final String parameterName; private final List<Parameter> parameters;
private final Type sourceType; private final List<Parameter> sourceParameters;
private final Type targetType; private final String resultName;
private final Type resultType;
private final Type returnType;
private Map<String, Mapping> mappings; private Map<String, Mapping> mappings;
private IterableMapping iterableMapping; private IterableMapping iterableMapping;
private MapMapping mapMapping; private MapMapping mapMapping;
public static Method forMethodRequiringImplementation(ExecutableElement executable, String parameterName, public static Method forMethodRequiringImplementation(ExecutableElement executable, List<Parameter> parameters,
Type sourceType, Type targetType, List<Parameter> sourceParameters, Type resultType,
String resultName, Type targetType,
Map<String, Mapping> mappings, Map<String, Mapping> mappings,
IterableMapping iterableMapping, MapMapping mapMapping) { IterableMapping iterableMapping, MapMapping mapMapping) {
return new Method( return new Method(
null, null,
executable, executable,
parameterName, parameters,
sourceType, sourceParameters,
resultType,
resultName,
targetType, targetType,
mappings, mappings,
iterableMapping, iterableMapping,
@ -64,8 +72,10 @@ public class Method {
return new Method( return new Method(
declaringMapper, declaringMapper,
executable, executable,
parameterName, Arrays.asList( new Parameter( parameterName, sourceType ) ),
sourceType, Arrays.asList( new Parameter( parameterName, sourceType ) ),
targetType,
null,
targetType, targetType,
Collections.<String, Mapping>emptyMap(), Collections.<String, Mapping>emptyMap(),
null, null,
@ -73,14 +83,18 @@ public class Method {
); );
} }
private Method(Type declaringMapper, ExecutableElement executable, String parameterName, Type sourceType, private Method(Type declaringMapper, ExecutableElement executable, List<Parameter> parameters,
Type targetType, Map<String, Mapping> mappings, IterableMapping iterableMapping, List<Parameter> sourceParameters, Type resultType, String resultName,
Type returnType,
Map<String, Mapping> mappings, IterableMapping iterableMapping,
MapMapping mapMapping) { MapMapping mapMapping) {
this.declaringMapper = declaringMapper; this.declaringMapper = declaringMapper;
this.executable = executable; this.executable = executable;
this.parameterName = parameterName; this.parameters = parameters;
this.sourceType = sourceType; this.sourceParameters = sourceParameters;
this.targetType = targetType; this.resultType = resultType;
this.resultName = resultName;
this.returnType = returnType;
this.mappings = mappings; this.mappings = mappings;
this.iterableMapping = iterableMapping; this.iterableMapping = iterableMapping;
this.mapMapping = mapMapping; this.mapMapping = mapMapping;
@ -105,16 +119,24 @@ public class Method {
return executable.getSimpleName().toString(); return executable.getSimpleName().toString();
} }
public String getParameterName() { public List<Parameter> getParameters() {
return parameterName; return parameters;
} }
public Type getSourceType() { public String getResultName() {
return sourceType; return resultName;
} }
public Type getTargetType() { public List<Parameter> getSourceParameters() {
return targetType; return sourceParameters;
}
public Type getResultType() {
return resultType;
}
public Type getReturnType() {
return returnType;
} }
public Map<String, Mapping> getMappings() { public Map<String, Mapping> getMappings() {
@ -143,16 +165,20 @@ public class Method {
public boolean reverses(Method method) { public boolean reverses(Method method) {
return return
equals( sourceType, method.getTargetType() ) && equals( getSingleSourceType(), method.getReturnType() )
equals( targetType, method.getSourceType() ); && equals( returnType, method.getSingleSourceType() );
}
public Type getSingleSourceType() {
return sourceParameters.size() == 1 ? sourceParameters.get( 0 ).getType() : null;
} }
public boolean isIterableMapping() { public boolean isIterableMapping() {
return sourceType.isIterableType() && targetType.isIterableType(); return getSingleSourceType().isIterableType() && resultType.isIterableType();
} }
public boolean isMapMapping() { public boolean isMapMapping() {
return sourceType.isMapType() && targetType.isMapType(); return getSingleSourceType().isMapType() && resultType.isMapType();
} }
private boolean equals(Object o1, Object o2) { private boolean equals(Object o1, Object o2) {
@ -161,6 +187,19 @@ public class Method {
@Override @Override
public String toString() { public String toString() {
return targetType + " " + getName() + "(" + sourceType + " " + parameterName + ")"; return returnType + " " + getName() + "(" + getParamsList() + ")";
}
private String getParamsList() {
StringBuilder sb = new StringBuilder();
for ( Iterator<Parameter> it = parameters.iterator(); it.hasNext(); ) {
Parameter param = it.next();
sb.append( param.getType() ).append( " " ).append( param.getName() );
if ( it.hasNext() ) {
sb.append( ", " );
}
}
return sb.toString();
} }
} }

View File

@ -29,10 +29,16 @@ public class Parameter {
private final String name; private final String name;
private final Type type; private final Type type;
private final boolean mappingTarget;
public Parameter(String name, Type type) { public Parameter(String name, Type type, boolean mappingTarget) {
this.name = name; this.name = name;
this.type = type; this.type = type;
this.mappingTarget = mappingTarget;
}
public Parameter(String name, Type type) {
this( name, type, false );
} }
public String getName() { public String getName() {
@ -43,8 +49,12 @@ public class Parameter {
return type; return type;
} }
public boolean isMappingTarget() {
return mappingTarget;
}
@Override @Override
public String toString() { public String toString() {
return type.toString() + " " + name; return ( mappingTarget ? "@MappingTarget " : "" ) + type.toString() + " " + name;
} }
} }

View File

@ -54,6 +54,7 @@ import org.mapstruct.ap.model.Type;
import org.mapstruct.ap.model.TypeConversion; import org.mapstruct.ap.model.TypeConversion;
import org.mapstruct.ap.model.source.Mapping; import org.mapstruct.ap.model.source.Mapping;
import org.mapstruct.ap.model.source.Method; import org.mapstruct.ap.model.source.Method;
import org.mapstruct.ap.model.source.Parameter;
import org.mapstruct.ap.util.Executables; import org.mapstruct.ap.util.Executables;
import org.mapstruct.ap.util.Filters; import org.mapstruct.ap.util.Filters;
import org.mapstruct.ap.util.Strings; import org.mapstruct.ap.util.Strings;
@ -200,19 +201,14 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
Map<String, Mapping> mappings = method.getMappings(); Map<String, Mapping> mappings = method.getMappings();
TypeElement returnTypeElement = (TypeElement) typeUtils.asElement( method.getExecutable().getReturnType() ); TypeElement resultTypeElement = elementUtils.getTypeElement( method.getResultType().getCanonicalName() );
TypeElement parameterElement = (TypeElement) typeUtils.asElement( TypeElement parameterElement = elementUtils.getTypeElement( method.getSingleSourceType().getCanonicalName() );
method.getExecutable()
.getParameters()
.get( 0 )
.asType()
);
List<ExecutableElement> sourceGetters = Filters.getterMethodsIn( List<ExecutableElement> sourceGetters = Filters.getterMethodsIn(
elementUtils.getAllMembers( parameterElement ) elementUtils.getAllMembers( parameterElement )
); );
List<ExecutableElement> targetSetters = Filters.setterMethodsIn( List<ExecutableElement> targetSetters = Filters.setterMethodsIn(
elementUtils.getAllMembers( returnTypeElement ) elementUtils.getAllMembers( resultTypeElement )
); );
Set<String> sourceProperties = executables.getPropertyNames( Set<String> sourceProperties = executables.getPropertyNames(
@ -257,9 +253,11 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
return new BeanMappingMethod( return new BeanMappingMethod(
method.getName(), method.getName(),
method.getParameterName(), method.getParameters(),
method.getSourceType(), method.getSourceParameters(),
method.getTargetType(), method.getResultType(),
method.getResultName(),
method.getReturnType(),
propertyMappings propertyMappings
); );
} }
@ -302,7 +300,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
String.format( String.format(
"Unknown property \"%s\" in parameter type %s.", "Unknown property \"%s\" in parameter type %s.",
mappedProperty.getSourceName(), mappedProperty.getSourceName(),
method.getSourceType() method.getSingleSourceType()
), ),
method.getExecutable(), method.getExecutable(),
mappedProperty.getMirror(), mappedProperty.getMirror(),
@ -315,7 +313,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
String.format( String.format(
"Unknown property \"%s\" in return type %s.", "Unknown property \"%s\" in return type %s.",
mappedProperty.getTargetName(), mappedProperty.getTargetName(),
method.getTargetType() method.getResultType()
), ),
method.getExecutable(), method.getExecutable(),
mappedProperty.getMirror(), mappedProperty.getMirror(),
@ -328,19 +326,20 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
private PropertyMapping getPropertyMapping(List<Method> methods, Method method, ExecutableElement getterMethod, private PropertyMapping getPropertyMapping(List<Method> methods, Method method, ExecutableElement getterMethod,
ExecutableElement setterMethod, String dateFormat) { ExecutableElement setterMethod, String dateFormat) {
Type sourceType = executables.retrieveReturnType( getterMethod ); Type sourceType = executables.retrieveReturnType( getterMethod );
Type targetType = executables.retrieveParameter( setterMethod ).getType(); Type targetType = executables.retrieveSingleParameter( setterMethod ).getType();
MappingMethodReference propertyMappingMethod = getMappingMethodReference( methods, sourceType, targetType ); MappingMethodReference propertyMappingMethod = getMappingMethodReference( methods, sourceType, targetType );
TypeConversion conversion = getConversion( TypeConversion conversion = getConversion(
sourceType, sourceType,
targetType, targetType,
dateFormat, dateFormat,
method.getParameterName() + "." + getterMethod.getSimpleName().toString() + "()" method.getSourceParameters().get( 0 ).getName() + "."
+ getterMethod.getSimpleName().toString() + "()"
); );
PropertyMapping property = new PropertyMapping( PropertyMapping property = new PropertyMapping(
method.getParameterName(), method.getSourceParameters().get( 0 ).getName(),
Introspector.decapitalize( method.getTargetType().getName() ), method.getResultName(),
executables.getPropertyName( getterMethod ), executables.getPropertyName( getterMethod ),
getterMethod.getSimpleName().toString(), getterMethod.getSimpleName().toString(),
sourceType, sourceType,
@ -360,8 +359,8 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
} }
private MappingMethod getIterableMappingMethod(List<Method> methods, Method method) { private MappingMethod getIterableMappingMethod(List<Method> methods, Method method) {
Type sourceElementType = method.getSourceType().getTypeParameters().get( 0 ); Type sourceElementType = method.getSourceParameters().get( 0 ).getType().getTypeParameters().get( 0 );
Type targetElementType = method.getTargetType().getTypeParameters().get( 0 ); Type targetElementType = method.getResultType().getTypeParameters().get( 0 );
TypeConversion conversion = getConversion( TypeConversion conversion = getConversion(
sourceElementType, sourceElementType,
@ -372,19 +371,24 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
return new IterableMappingMethod( return new IterableMappingMethod(
method.getName(), method.getName(),
method.getParameterName(), method.getParameters(),
method.getSourceType(), method.getSourceParameters(),
method.getTargetType(), method.getResultType(),
method.getResultName(),
method.getReturnType(),
getMappingMethodReference( methods, sourceElementType, targetElementType ), getMappingMethodReference( methods, sourceElementType, targetElementType ),
conversion conversion
); );
} }
private MappingMethod getMapMappingMethod(List<Method> methods, Method method) { private MappingMethod getMapMappingMethod(List<Method> methods, Method method) {
Type sourceKeyType = method.getSourceType().getTypeParameters().get( 0 ); List<Type> sourceTypeParams = method.getSourceParameters().get( 0 ).getType().getTypeParameters();
Type sourceValueType = method.getSourceType().getTypeParameters().get( 1 ); Type sourceKeyType = sourceTypeParams.get( 0 );
Type targetKeyType = method.getTargetType().getTypeParameters().get( 0 ); Type sourceValueType = sourceTypeParams.get( 1 );
Type targetValueType = method.getTargetType().getTypeParameters().get( 1 );
List<Type> resultTypeParams = method.getResultType().getTypeParameters();
Type targetKeyType = resultTypeParams.get( 0 );
Type targetValueType = resultTypeParams.get( 1 );
String keyDateFormat = method.getMapMapping() != null ? method.getMapMapping().getKeyFormat() : null; String keyDateFormat = method.getMapMapping() != null ? method.getMapMapping().getKeyFormat() : null;
String valueDateFormat = method.getMapMapping() != null ? method.getMapMapping().getValueFormat() : null; String valueDateFormat = method.getMapMapping() != null ? method.getMapMapping().getValueFormat() : null;
@ -406,9 +410,11 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
return new MapMappingMethod( return new MapMappingMethod(
method.getName(), method.getName(),
method.getParameterName(), method.getParameters(),
method.getSourceType(), method.getSourceParameters(),
method.getTargetType(), method.getResultType(),
method.getResultName(),
method.getReturnType(),
keyMappingMethod, keyMappingMethod,
keyConversion, keyConversion,
valueMappingMethod, valueMappingMethod,
@ -429,13 +435,13 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
private MappingMethodReference getMappingMethodReference(Iterable<Method> methods, Type parameterType, private MappingMethodReference getMappingMethodReference(Iterable<Method> methods, Type parameterType,
Type returnType) { Type returnType) {
for ( Method oneMethod : methods ) { for ( Method oneMethod : methods ) {
if ( oneMethod.getSourceType().equals( parameterType ) && oneMethod.getTargetType().equals( returnType ) ) { Parameter singleSourceParam = oneMethod.getSourceParameters().get( 0 );
if ( singleSourceParam.getType().equals( parameterType ) &&
oneMethod.getReturnType().equals( returnType ) ) {
return new MappingMethodReference( return new MappingMethodReference(
oneMethod.getDeclaringMapper(), oneMethod.getDeclaringMapper(),
oneMethod.getName(), oneMethod.getName()
oneMethod.getParameterName(),
oneMethod.getSourceType(),
oneMethod.getTargetType()
); );
} }
} }

View File

@ -18,6 +18,7 @@
*/ */
package org.mapstruct.ap.processor; package org.mapstruct.ap.processor;
import java.beans.Introspector;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -115,19 +116,26 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
} }
private Method getMethod(TypeElement element, ExecutableElement method, boolean implementationRequired) { private Method getMethod(TypeElement element, ExecutableElement method, boolean implementationRequired) {
Parameter parameter = executables.retrieveParameter( method ); List<Parameter> parameters = executables.retrieveParameters( method );
Type returnType = executables.retrieveReturnType( method ); Type returnType = executables.retrieveReturnType( method );
//add method with property mappings if an implementation needs to be generated //add method with property mappings if an implementation needs to be generated
if ( implementationRequired ) { if ( implementationRequired ) {
boolean isValid = checkParameterAndReturnType( method, parameter.getType(), returnType ); List<Parameter> sourceParameters = extractSourceParameters( parameters );
Parameter targetParameter = extractTargetParameter( parameters );
Type resultType = selectResultType( returnType, targetParameter );
boolean isValid =
checkParameterAndReturnType( method, sourceParameters, targetParameter, resultType, returnType );
if ( isValid ) { if ( isValid ) {
return return
Method.forMethodRequiringImplementation( Method.forMethodRequiringImplementation(
method, method,
parameter.getName(), parameters,
parameter.getType(), sourceParameters,
resultType,
selectResultName( targetParameter, resultType ),
returnType, returnType,
getMappings( method ), getMappings( method ),
IterableMapping.fromPrism( IterableMappingPrism.getInstanceOn( method ) ), IterableMapping.fromPrism( IterableMappingPrism.getInstanceOn( method ) ),
@ -139,20 +147,102 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
} }
} }
//otherwise add reference to existing mapper method //otherwise add reference to existing mapper method
else { else if ( parameters.size() == 1 ) {
return return
Method.forReferencedMethod( Method.forReferencedMethod(
typeUtil.getType( typeUtils.getDeclaredType( element ) ), typeUtil.getType( typeUtils.getDeclaredType( element ) ),
method, method,
parameter.getName(), parameters.get( 0 ).getName(),
parameter.getType(), parameters.get( 0 ).getType(),
returnType returnType
); );
} }
else {
return null;
}
} }
private boolean checkParameterAndReturnType(ExecutableElement method, Type parameterType, Type returnType) { private Parameter extractTargetParameter(List<Parameter> parameters) {
if ( parameterType.isIterableType() && !returnType.isIterableType() ) { for ( Parameter param : parameters ) {
if ( param.isMappingTarget() ) {
return param;
}
}
return null;
}
private List<Parameter> extractSourceParameters(List<Parameter> parameters) {
List<Parameter> sourceParameters = new ArrayList<Parameter>( parameters.size() );
for ( Parameter param : parameters ) {
if ( !param.isMappingTarget() ) {
sourceParameters.add( param );
}
}
return sourceParameters;
}
private String selectResultName(Parameter targetParameter, Type resultType) {
if ( null != targetParameter ) {
return targetParameter.getName();
}
else {
return Introspector.decapitalize( resultType.getName() );
}
}
private Type selectResultType(Type returnType, Parameter targetParameter) {
if ( null != targetParameter ) {
return targetParameter.getType();
}
else {
return returnType;
}
}
private boolean checkParameterAndReturnType(ExecutableElement method, List<Parameter> sourceParameters,
Parameter targetParameter, Type resultType, Type returnType) {
if ( sourceParameters.isEmpty() ) {
messager.printMessage( Kind.ERROR, "Can't generate mapping method with no input arguments.", method );
return false;
}
if ( sourceParameters.size() > 1 ) {
messager.printMessage(
Kind.ERROR,
"Mappings from more than one source objects are not yet supported.",
method
);
return false;
}
if ( targetParameter != null && ( sourceParameters.size() + 1 != method.getParameters().size() ) ) {
messager.printMessage(
Kind.ERROR,
"Can't generate mapping method with more than one @MappingTarget parameter.",
method
);
return false;
}
if ( resultType == Type.VOID ) {
messager.printMessage( Kind.ERROR, "Can't generate mapping method with return type void.", method );
return false;
}
if ( returnType != Type.VOID && !typeUtil.isAssignable( resultType, returnType ) ) {
messager.printMessage(
Kind.ERROR,
"The result type is not assignable to the the return type.",
method
);
return false;
}
Type parameterType = sourceParameters.get( 0 ).getType();
if ( parameterType.isIterableType() && !resultType.isIterableType() ) {
messager.printMessage( messager.printMessage(
Kind.ERROR, Kind.ERROR,
"Can't generate mapping method from iterable type to non-iterable type.", "Can't generate mapping method from iterable type to non-iterable type.",
@ -161,7 +251,7 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
return false; return false;
} }
if ( !parameterType.isIterableType() && returnType.isIterableType() ) { if ( !parameterType.isIterableType() && resultType.isIterableType() ) {
messager.printMessage( messager.printMessage(
Kind.ERROR, Kind.ERROR,
"Can't generate mapping method from non-iterable type to iterable type.", "Can't generate mapping method from non-iterable type to iterable type.",
@ -175,7 +265,7 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
return false; return false;
} }
if ( returnType.isPrimitive() ) { if ( resultType.isPrimitive() ) {
messager.printMessage( Kind.ERROR, "Can't generate mapping method with primitive return type.", method ); messager.printMessage( Kind.ERROR, "Can't generate mapping method with primitive return type.", method );
return false; return false;
} }

View File

@ -19,13 +19,16 @@
package org.mapstruct.ap.util; package org.mapstruct.ap.util;
import java.beans.Introspector; import java.beans.Introspector;
import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.VariableElement; import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeKind;
import org.mapstruct.ap.MappingTargetPrism;
import org.mapstruct.ap.model.Type; import org.mapstruct.ap.model.Type;
import org.mapstruct.ap.model.source.Parameter; import org.mapstruct.ap.model.source.Parameter;
@ -105,7 +108,7 @@ public class Executables {
return propertyNames; return propertyNames;
} }
public Parameter retrieveParameter(ExecutableElement method) { public Parameter retrieveSingleParameter(ExecutableElement method) {
List<? extends VariableElement> parameters = method.getParameters(); List<? extends VariableElement> parameters = method.getParameters();
if ( parameters.size() != 1 ) { if ( parameters.size() != 1 ) {
@ -117,10 +120,42 @@ public class Executables {
return new Parameter( return new Parameter(
parameter.getSimpleName().toString(), parameter.getSimpleName().toString(),
typeUtil.retrieveType( parameter.asType() ) typeUtil.retrieveType( parameter.asType() ),
false
); );
} }
public List<Parameter> retrieveParameters(ExecutableElement method) {
List<? extends VariableElement> parameters = method.getParameters();
List<Parameter> result = new ArrayList<Parameter>( parameters.size() );
boolean mappingTargetDefined = false;
for ( Iterator<? extends VariableElement> it = parameters.iterator(); it.hasNext(); ) {
VariableElement parameter = it.next();
boolean isExplicitMappingTarget = null != MappingTargetPrism.getInstanceOn( parameter );
mappingTargetDefined |= isExplicitMappingTarget;
result
.add(
new Parameter(
parameter.getSimpleName().toString(),
typeUtil.retrieveType( parameter.asType() ),
// the parameter is a mapping target, if it was either defined explicitly or if if this is the
// last parameter in a multi-argument void method
isExplicitMappingTarget
|| ( !mappingTargetDefined && isMultiArgVoidMethod( method ) && !it.hasNext() )
)
);
}
return result;
}
public boolean isMultiArgVoidMethod(ExecutableElement method) {
return method.getParameters().size() > 1 && Type.VOID == retrieveReturnType( method );
}
public Type retrieveReturnType(ExecutableElement method) { public Type retrieveReturnType(ExecutableElement method) {
return typeUtil.retrieveType( method.getReturnType() ); return typeUtil.retrieveType( method.getReturnType() );
} }

View File

@ -23,6 +23,7 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import javax.lang.model.element.ElementKind; import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType; import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeMirror;
@ -56,6 +57,7 @@ public class TypeUtil {
} }
return new Type( return new Type(
( (TypeElement) type.asElement() ).getQualifiedName().toString(),
elementUtils.getPackageOf( type.asElement() ).toString(), elementUtils.getPackageOf( type.asElement() ).toString(),
type.asElement().getSimpleName().toString(), type.asElement().getSimpleName().toString(),
type.asElement().getKind() == ElementKind.ENUM, type.asElement().getKind() == ElementKind.ENUM,
@ -85,8 +87,42 @@ public class TypeUtil {
else if ( mirror.getKind() == TypeKind.DECLARED ) { else if ( mirror.getKind() == TypeKind.DECLARED ) {
return getType( ( (DeclaredType) mirror ) ); return getType( ( (DeclaredType) mirror ) );
} }
else if ( mirror.getKind() == TypeKind.VOID ) {
return Type.VOID;
}
else { else {
return new Type( mirror.toString() ); return new Type( mirror.toString() );
} }
} }
/**
* @param type1 first type
* @param type2 second type
*
* @return {@code true} if and only if the first type is assignable to the second
*/
public boolean isAssignable(Type type1, Type type2) {
if ( type1.equals( type2 ) ) {
return true;
}
TypeMirror mirror1 = toTypeMirror( type1 );
TypeMirror mirror2 = toTypeMirror( type2 );
return null != mirror1 && null != mirror2 && typeUtils.isAssignable( mirror1, mirror2 );
}
private TypeMirror toTypeMirror(Type type) {
TypeElement rawType = elementUtils.getTypeElement( type.getCanonicalName() );
if ( null == rawType ) {
return null;
}
TypeMirror[] parameters = new TypeMirror[type.getTypeParameters().size()];
for ( int i = 0; i < type.getTypeParameters().size(); i++ ) {
parameters[i] = toTypeMirror( type.getTypeParameters().get( i ) );
}
return typeUtils.getDeclaredType( rawType, parameters );
}
} }

View File

@ -19,16 +19,19 @@
--> -->
@Override @Override
public ${targetType.name} ${name}(${sourceType.name} ${parameterName}) { public ${returnType.name} ${name}(<#list parameters as param>${param.type.name} ${param.name}<#if param_has_next>, </#if></#list>) {
if ( ${parameterName} == null ) { if ( ${sourceParameters[0].name} == null ) {
return null; return<#if returnType.name != "void"> null</#if>;
} }
<#if !existingInstanceMapping>
${targetType.name} ${targetType.name?uncap_first} = new ${targetType.name}(); ${resultType.name} ${resultName} = new ${resultType.name}();
</#if>
<#list propertyMappings as propertyMapping> <#list propertyMappings as propertyMapping>
<@includeModel object=propertyMapping/> <@includeModel object=propertyMapping/>
</#list> </#list>
<#if returnType.name != "void">
return ${targetType.name?uncap_first}; return ${resultName};
</#if>
} }

View File

@ -19,23 +19,27 @@
--> -->
@Override @Override
public <@includeModel object=targetType/> ${name}(<@includeModel object=sourceType/> ${parameterName}) { public <@includeModel object=returnType/> ${name}(<#list parameters as param><@includeModel object=param.type/> ${param.name}<#if param_has_next>, </#if></#list>) {
if ( ${parameterName} == null ) { if ( ${sourceParameters[0].name} == null ) {
return null; return<#if returnType.name != "void"> null</#if>;
} }
<#if existingInstanceMapping>
${resultName}.clear();
<#else>
<#-- Use the interface type on the left side, except it is java.lang.Iterable; use the implementation type - if present - on the right side --> <#-- Use the interface type on the left side, except it is java.lang.Iterable; use the implementation type - if present - on the right side -->
<#if targetType.name == "Iterable" && targetType.packageName == "java.lang">${targetType.iterableImplementationType.name}<#else>${targetType.name}</#if><<@includeModel object=targetType.typeParameters[0]/>> ${targetType.name?uncap_first} = new <#if targetType.iterableImplementationType??>${targetType.iterableImplementationType.name}<#else>${targetType.name}</#if><<@includeModel object=targetType.typeParameters[0]/>>(); <#if resultType.name == "Iterable" && resultType.packageName == "java.lang">${resultType.iterableImplementationType.name}<#else>${resultType.name}</#if><<@includeModel object=resultType.typeParameters[0]/>> ${resultName} = new <#if resultType.iterableImplementationType??>${resultType.iterableImplementationType.name}<#else>${resultType.name}</#if><<@includeModel object=resultType.typeParameters[0]/>>();
</#if>
for ( <@includeModel object=sourceType.typeParameters[0]/> ${sourceType.typeParameters[0].name?uncap_first} : ${parameterName} ) { for ( <@includeModel object=sourceParameters[0].type.typeParameters[0]/> ${sourceParameters[0].type.typeParameters[0].name?uncap_first} : ${sourceParameters[0].name} ) {
<#if elementMappingMethod??> <#if elementMappingMethod??>
${targetType.name?uncap_first}.add( <@includeModel object=elementMappingMethod input="${sourceType.typeParameters[0].name?uncap_first}"/> ); ${resultName}.add( <@includeModel object=elementMappingMethod input="${sourceParameters[0].type.typeParameters[0].name?uncap_first}"/> );
<#else> <#else>
<#if (conversion.exceptionTypes?size == 0) > <#if (conversion.exceptionTypes?size == 0) >
${targetType.name?uncap_first}.add( <@includeModel object=conversion/> ); ${resultName}.add( <@includeModel object=conversion/> );
<#else> <#else>
try { try {
${targetType.name?uncap_first}.add( <@includeModel object=conversion/> ); ${resultName}.add( <@includeModel object=conversion/> );
} }
<#list conversion.exceptionTypes as exceptionType> <#list conversion.exceptionTypes as exceptionType>
catch( ${exceptionType.name} e ) { catch( ${exceptionType.name} e ) {
@ -45,6 +49,8 @@
</#if> </#if>
</#if> </#if>
} }
<#if returnType.name != "void">
return ${targetType.name?uncap_first}; return ${resultName};
</#if>
} }

View File

@ -19,23 +19,27 @@
--> -->
@Override @Override
public <@includeModel object=targetType /> ${name}(<@includeModel object=sourceType /> ${parameterName}) { public <@includeModel object=returnType /> ${name}(<#list parameters as param><@includeModel object=param.type/> ${param.name}<#if param_has_next>, </#if></#list>) {
if ( ${parameterName} == null ) { if ( ${sourceParameters[0].name} == null ) {
return null; return<#if returnType.name != "void"> null</#if>;
} }
<@includeModel object=targetType /> ${returnValueName} = new <#if targetType.mapImplementationType??><@includeModel object=targetType.mapImplementationType /><#else><@includeModel object=targetType /></#if>(); <#if existingInstanceMapping>
${resultName}.clear();
<#else>
<@includeModel object=resultType /> ${resultName} = new <#if resultType.mapImplementationType??><@includeModel object=resultType.mapImplementationType /><#else><@includeModel object=resultType /></#if>();
</#if>
for ( Map.Entry<<#list sourceType.typeParameters as typeParameter><@includeModel object=typeParameter /><#if typeParameter_has_next>, </#if></#list>> entry : ${parameterName}.entrySet() ) { for ( Map.Entry<<#list sourceParameters[0].type.typeParameters as typeParameter><@includeModel object=typeParameter /><#if typeParameter_has_next>, </#if></#list>> entry : ${sourceParameters[0].name}.entrySet() ) {
<#-- key --> <#-- key -->
<#if keyMappingMethod??> <#if keyMappingMethod??>
<@includeModel object=targetType.typeParameters[0]/> key = <@includeModel object=keyMappingMethod input="entry.getKey()"/>; <@includeModel object=resultType.typeParameters[0]/> key = <@includeModel object=keyMappingMethod input="entry.getKey()"/>;
<#elseif keyConversion??> <#elseif keyConversion??>
<#if (keyConversion.exceptionTypes?size == 0) > <#if (keyConversion.exceptionTypes?size == 0) >
<@includeModel object=targetType.typeParameters[0]/> key = <@includeModel object=keyConversion/>; <@includeModel object=resultType.typeParameters[0]/> key = <@includeModel object=keyConversion/>;
<#else> <#else>
<@includeModel object=targetType.typeParameters[0]/> key; <@includeModel object=resultType.typeParameters[0]/> key;
try { try {
key = <@includeModel object=keyConversion/>; key = <@includeModel object=keyConversion/>;
} }
@ -46,16 +50,16 @@
</#list> </#list>
</#if> </#if>
<#else> <#else>
<@includeModel object=targetType.typeParameters[0]/> key = entry.getKey(); <@includeModel object=resultType.typeParameters[0]/> key = entry.getKey();
</#if> </#if>
<#-- value --> <#-- value -->
<#if valueMappingMethod??> <#if valueMappingMethod??>
<@includeModel object=targetType.typeParameters[1]/> value = <@includeModel object=valueMappingMethod input="entry.getValue()"/>; <@includeModel object=resultType.typeParameters[1]/> value = <@includeModel object=valueMappingMethod input="entry.getValue()"/>;
<#elseif valueConversion??> <#elseif valueConversion??>
<#if (valueConversion.exceptionTypes?size == 0) > <#if (valueConversion.exceptionTypes?size == 0) >
<@includeModel object=targetType.typeParameters[1]/> value = <@includeModel object=valueConversion/>; <@includeModel object=resultType.typeParameters[1]/> value = <@includeModel object=valueConversion/>;
<#else> <#else>
<@includeModel object=targetType.typeParameters[1]/> value; <@includeModel object=resultType.typeParameters[1]/> value;
try { try {
value = <@includeModel object=valueConversion/>; value = <@includeModel object=valueConversion/>;
} }
@ -66,11 +70,13 @@
</#list> </#list>
</#if> </#if>
<#else> <#else>
<@includeModel object=targetType.typeParameters[1]/> value = entry.getValue(); <@includeModel object=resultType.typeParameters[1]/> value = entry.getValue();
</#if> </#if>
${returnValueName}.put( key, value ); ${resultName}.put( key, value );
} }
<#if returnType.name != "void">
return ${returnValueName}; return ${resultName};
</#if>
} }

View File

@ -18,8 +18,10 @@
*/ */
package org.mapstruct.ap.test.collection.defaultimplementation; package org.mapstruct.ap.test.collection.defaultimplementation;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import org.mapstruct.ap.testutil.IssueKey; import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.MapperTestBase; import org.mapstruct.ap.testutil.MapperTestBase;
@ -41,7 +43,7 @@ public class DefaultCollectionImplementationTest extends MapperTestBase {
@IssueKey("6") @IssueKey("6")
public void shouldUseDefaultImplementationForList() { public void shouldUseDefaultImplementationForList() {
Source source = new Source(); Source source = new Source();
source.setFooList( Arrays.asList( new SourceFoo( "Bob" ), new SourceFoo( "Alice" ) ) ); source.setFooList( createSourceFooList() );
Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source ); Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source );
assertThat( target ).isNotNull(); assertThat( target ).isNotNull();
@ -52,7 +54,7 @@ public class DefaultCollectionImplementationTest extends MapperTestBase {
@IssueKey("6") @IssueKey("6")
public void shouldUseDefaultImplementationForSet() { public void shouldUseDefaultImplementationForSet() {
Source source = new Source(); Source source = new Source();
source.setFooSet( new HashSet<SourceFoo>( Arrays.asList( new SourceFoo( "Bob" ), new SourceFoo( "Alice" ) ) ) ); source.setFooSet( new HashSet<SourceFoo>( createSourceFooList() ) );
Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source ); Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source );
assertThat( target ).isNotNull(); assertThat( target ).isNotNull();
@ -63,7 +65,7 @@ public class DefaultCollectionImplementationTest extends MapperTestBase {
@IssueKey("6") @IssueKey("6")
public void shouldUseDefaultImplementationForCollection() { public void shouldUseDefaultImplementationForCollection() {
Source source = new Source(); Source source = new Source();
source.setFooCollection( Arrays.asList( new SourceFoo( "Bob" ), new SourceFoo( "Alice" ) ) ); source.setFooCollection( createSourceFooList() );
Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source ); Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source );
assertThat( target ).isNotNull(); assertThat( target ).isNotNull();
@ -74,10 +76,49 @@ public class DefaultCollectionImplementationTest extends MapperTestBase {
@IssueKey("6") @IssueKey("6")
public void shouldUseDefaultImplementationForIterable() { public void shouldUseDefaultImplementationForIterable() {
Source source = new Source(); Source source = new Source();
source.setFooIterable( Arrays.asList( new SourceFoo( "Bob" ), new SourceFoo( "Alice" ) ) ); source.setFooIterable( createSourceFooList() );
Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source ); Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source );
assertThat( target ).isNotNull(); assertThat( target ).isNotNull();
assertThat( target.getFooIterable() ).containsOnly( new TargetFoo( "Bob" ), new TargetFoo( "Alice" ) ); Iterable<TargetFoo> fooIterable = target.getFooIterable();
assertResultList( fooIterable );
}
private void assertResultList(Iterable<TargetFoo> fooIterable) {
assertThat( fooIterable ).isNotNull();
assertThat( fooIterable ).containsOnly( new TargetFoo( "Bob" ), new TargetFoo( "Alice" ) );
}
private List<SourceFoo> createSourceFooList() {
return Arrays.asList( new SourceFoo( "Bob" ), new SourceFoo( "Alice" ) );
}
@Test
@IssueKey("19")
public void existingMapping1() {
List<TargetFoo> target = new ArrayList<TargetFoo>();
SourceTargetMapper.INSTANCE.sourceFoosToTargetFoos1( createSourceFooList(), target );
assertResultList( target );
}
@Test
@IssueKey("19")
public void existingMapping2() {
List<TargetFoo> target = new ArrayList<TargetFoo>();
SourceTargetMapper.INSTANCE.sourceFoosToTargetFoos2( target, createSourceFooList() );
assertResultList( target );
}
@Test
@IssueKey("19")
public void existingMapping3() {
List<TargetFoo> target = new ArrayList<TargetFoo>();
Iterable<TargetFoo> result =
SourceTargetMapper.INSTANCE.sourceFoosToTargetFoos3( createSourceFooList(), target );
assertThat( target == result ).isTrue();
assertResultList( target );
} }
} }

View File

@ -24,6 +24,7 @@ import java.util.Set;
import org.mapstruct.Mapper; import org.mapstruct.Mapper;
import org.mapstruct.Mappers; import org.mapstruct.Mappers;
import org.mapstruct.MappingTarget;
@Mapper @Mapper
public interface SourceTargetMapper { public interface SourceTargetMapper {
@ -41,4 +42,11 @@ public interface SourceTargetMapper {
Collection<TargetFoo> sourceFoosToTargetFoos(Collection<SourceFoo> foos); Collection<TargetFoo> sourceFoosToTargetFoos(Collection<SourceFoo> foos);
Iterable<TargetFoo> sourceFoosToTargetFoos(Iterable<SourceFoo> foos); Iterable<TargetFoo> sourceFoosToTargetFoos(Iterable<SourceFoo> foos);
void sourceFoosToTargetFoos1(Iterable<SourceFoo> sourceFoos, List<TargetFoo> targetFoos);
void sourceFoosToTargetFoos2(@MappingTarget List<TargetFoo> targetFoos, Iterable<SourceFoo> sourceFoos);
Iterable<TargetFoo> sourceFoosToTargetFoos3(Iterable<SourceFoo> sourceFoos,
@MappingTarget List<TargetFoo> targetFoos);
} }

View File

@ -58,12 +58,55 @@ public class MapMappingTest extends MapperTestBase {
@Test @Test
public void shouldCreateReverseMapMethodImplementation() { public void shouldCreateReverseMapMethodImplementation() {
Map<String, String> values = new HashMap<String, String>(); Map<String, String> values = createStringStringMap();
values.put( "42", "01.01.1980" );
values.put( "121", "20.07.2013" );
Map<Long, Date> target = SourceTargetMapper.INSTANCE.stringStringMapToLongDateMap( values ); Map<Long, Date> target = SourceTargetMapper.INSTANCE.stringStringMapToLongDateMap( values );
assertResult( target );
}
@Test
@IssueKey("19")
public void shouldCreateReverseMapMethodImplementation1() {
Map<String, String> values = createStringStringMap();
Map<Long, Date> target = new HashMap<Long, Date>();
target.put( 66L, new GregorianCalendar( 2013, 7, 16 ).getTime() );
SourceTargetMapper.INSTANCE.stringStringMapToLongDateMap( values, target );
assertResult( target );
}
@Test
@IssueKey("19")
public void shouldCreateReverseMapMethodImplementation2() {
Map<String, String> values = createStringStringMap();
Map<Long, Date> target = new HashMap<Long, Date>();
target.put( 66L, new GregorianCalendar( 2013, 7, 16 ).getTime() );
SourceTargetMapper.INSTANCE.stringStringMapToLongDateMap2( target, values );
assertResult( target );
}
@Test
@IssueKey("19")
public void shouldCreateReverseMapMethodImplementation3() {
Map<String, String> values = createStringStringMap();
Map<Long, Date> target = new HashMap<Long, Date>();
target.put( 66L, new GregorianCalendar( 2013, 7, 16 ).getTime() );
Map<Long, Date> returnedTarget = SourceTargetMapper.INSTANCE.stringStringMapToLongDateMap3( values, target );
assertThat( target ).isSameAs( returnedTarget );
assertResult( target );
}
private void assertResult(Map<Long, Date> target) {
assertThat( target ).isNotNull(); assertThat( target ).isNotNull();
assertThat( target ).hasSize( 2 ); assertThat( target ).hasSize( 2 );
assertThat( target ).includes( assertThat( target ).includes(
@ -72,6 +115,13 @@ public class MapMappingTest extends MapperTestBase {
); );
} }
private Map<String, String> createStringStringMap() {
Map<String, String> values = new HashMap<String, String>();
values.put( "42", "01.01.1980" );
values.put( "121", "20.07.2013" );
return values;
}
@Test @Test
public void shouldInvokeMapMethodImplementationForMapTypedProperty() { public void shouldInvokeMapMethodImplementationForMapTypedProperty() {
Map<Long, Date> values = new HashMap<Long, Date>(); Map<Long, Date> values = new HashMap<Long, Date>();
@ -94,9 +144,7 @@ public class MapMappingTest extends MapperTestBase {
@Test @Test
public void shouldInvokeReverseMapMethodImplementationForMapTypedProperty() { public void shouldInvokeReverseMapMethodImplementationForMapTypedProperty() {
Map<String, String> values = new HashMap<String, String>(); Map<String, String> values = createStringStringMap();
values.put( "42", "01.01.1980" );
values.put( "121", "20.07.2013" );
Target target = new Target(); Target target = new Target();
target.setValues( values ); target.setValues( values );

View File

@ -24,17 +24,27 @@ import java.util.Map;
import org.mapstruct.MapMapping; import org.mapstruct.MapMapping;
import org.mapstruct.Mapper; import org.mapstruct.Mapper;
import org.mapstruct.Mappers; import org.mapstruct.Mappers;
import org.mapstruct.MappingTarget;
@Mapper(uses = CustomNumberMapper.class) @Mapper(uses = CustomNumberMapper.class)
public interface SourceTargetMapper { public interface SourceTargetMapper {
SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class ); SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class );
@MapMapping( valueDateFormat = "dd.MM.yyyy" ) @MapMapping(valueDateFormat = "dd.MM.yyyy")
Map<String, String> longDateMapToStringStringMap(Map<Long, Date> source); Map<String, String> longDateMapToStringStringMap(Map<Long, Date> source);
Map<Long, Date> stringStringMapToLongDateMap(Map<String, String> source); Map<Long, Date> stringStringMapToLongDateMap(Map<String, String> source);
@MapMapping(valueDateFormat = "dd.MM.yyyy")
void stringStringMapToLongDateMap(Map<String, String> source, Map<Long, Date> target);
@MapMapping(valueDateFormat = "dd.MM.yyyy")
void stringStringMapToLongDateMap2(@MappingTarget Map<Long, Date> target, Map<String, String> source);
@MapMapping(valueDateFormat = "dd.MM.yyyy")
Map<Long, Date> stringStringMapToLongDateMap3(Map<String, String> source, @MappingTarget Map<Long, Date> target);
Target sourceToTarget(Source source); Target sourceToTarget(Source source);
Source targetToSource(Target target); Source targetToSource(Target target);

View File

@ -36,17 +36,61 @@ public class InheritanceTest extends MapperTestBase {
@Test @Test
@IssueKey("17") @IssueKey("17")
public void shouldMapAttributeFromSuperType() { public void shouldMapAttributeFromSuperType() {
SourceExt source = new SourceExt(); SourceExt source = createSource();
source.setFoo( 42 );
source.setBar( 23L );
TargetExt target = SourceTargetMapper.INSTANCE.sourceToTarget( source ); TargetExt target = SourceTargetMapper.INSTANCE.sourceToTarget( source );
assertResult( target );
}
@Test
@IssueKey("19")
public void existingMapping1() {
SourceExt source = createSource();
TargetExt target = new TargetExt();
SourceTargetMapper.INSTANCE.sourceToTarget1( source, target );
assertResult( target );
}
@Test
@IssueKey("19")
public void existingMapping2() {
SourceExt source = createSource();
TargetExt target = new TargetExt();
SourceTargetMapper.INSTANCE.sourceToTarget2( target, source );
assertResult( target );
}
@Test
@IssueKey("19")
public void existingMapping3() {
SourceExt source = createSource();
TargetExt target = new TargetExt();
TargetBase result = SourceTargetMapper.INSTANCE.sourceToTarget3( source, target );
assertThat( target ).isSameAs( result );
assertResult( target );
}
private void assertResult(TargetExt target) {
assertThat( target ).isNotNull(); assertThat( target ).isNotNull();
assertThat( target.getFoo() ).isEqualTo( Long.valueOf( 42 ) ); assertThat( target.getFoo() ).isEqualTo( Long.valueOf( 42 ) );
assertThat( target.getBar() ).isEqualTo( 23 ); assertThat( target.getBar() ).isEqualTo( 23 );
} }
private SourceExt createSource() {
SourceExt source = new SourceExt();
source.setFoo( 42 );
source.setBar( 23L );
return source;
}
@Test @Test
@IssueKey("17") @IssueKey("17")
public void shouldReverseMapAttributeFromSuperType() { public void shouldReverseMapAttributeFromSuperType() {

View File

@ -20,6 +20,7 @@ package org.mapstruct.ap.test.inheritance;
import org.mapstruct.Mapper; import org.mapstruct.Mapper;
import org.mapstruct.Mappers; import org.mapstruct.Mappers;
import org.mapstruct.MappingTarget;
@Mapper @Mapper
public interface SourceTargetMapper { public interface SourceTargetMapper {
@ -28,5 +29,11 @@ public interface SourceTargetMapper {
TargetExt sourceToTarget(SourceExt source); TargetExt sourceToTarget(SourceExt source);
void sourceToTarget1(SourceExt source, TargetExt target);
void sourceToTarget2(@MappingTarget TargetExt target, SourceExt source);
TargetBase sourceToTarget3(SourceExt source, @MappingTarget TargetExt target);
SourceExt targetToSource(TargetExt target); SourceExt targetToSource(TargetExt target);
} }