#1001 auto mapping support for update methods

This commit is contained in:
sjaakd 2017-01-08 21:43:37 +01:00
parent 5cb80cbb97
commit 0188dcbdcc
7 changed files with 121 additions and 34 deletions

View File

@ -635,14 +635,24 @@ public class PropertyMapping extends ModelElement {
if ( sourceType.isPrimitive() || targetType.isPrimitive() ) { if ( sourceType.isPrimitive() || targetType.isPrimitive() ) {
return null; return null;
} }
String name = getName( sourceType, targetType ); String name = getName( sourceType, targetType );
List<Parameter> parameters = new ArrayList( method.getContextParameters() );
Type returnType;
if ( method.isUpdateMethod() ) {
parameters.add( Parameter.forForgedMappingTarget( targetType ) );
returnType = ctx.getTypeFactory().createVoidType();
}
else {
returnType = targetType;
}
ForgedMethod forgedMethod = new ForgedMethod( ForgedMethod forgedMethod = new ForgedMethod(
name, name,
sourceType, sourceType,
targetType, returnType,
method.getMapperConfiguration(), method.getMapperConfiguration(),
method.getExecutable(), method.getExecutable(),
method.getContextParameters(), parameters,
getForgedMethodHistory( sourceRHS ) getForgedMethodHistory( sourceRHS )
); );

View File

@ -125,6 +125,15 @@ public class Parameter extends ModelElement {
ContextPrism.getInstanceOn( element ) != null ); ContextPrism.getInstanceOn( element ) != null );
} }
public static Parameter forForgedMappingTarget(Type parameterType) {
return new Parameter(
"mappingTarget",
parameterType,
true,
false,
false);
}
/** /**
* @param parameters the parameters to filter * @param parameters the parameters to filter
* @return the parameters from the given list that are considered 'source parameters' * @return the parameters from the given list that are considered 'source parameters'
@ -156,4 +165,24 @@ public class Parameter extends ModelElement {
return contextParameters; return contextParameters;
} }
public static Parameter getMappingTargetParameter(List<Parameter> parameters) {
for ( Parameter parameter : parameters ) {
if ( parameter.isMappingTarget() ) {
return parameter;
}
}
return null;
}
public static Parameter getTargetTypeParameter(List<Parameter> parameters) {
for ( Parameter parameter : parameters ) {
if ( parameter.isTargetType() ) {
return parameter;
}
}
return null;
}
} }

View File

@ -493,6 +493,15 @@ public class TypeFactory {
return collectionOrMap; return collectionOrMap;
} }
/**
* creates a void return type
*
* @return void type
*/
public Type createVoidType() {
return getType( typeUtils.getNoType( TypeKind.VOID ) );
}
/** /**
* Establishes the type bound: * Establishes the type bound:
* <ol> * <ol>

View File

@ -47,20 +47,21 @@ public class ForgedMethod implements Method {
private final List<Parameter> sourceParameters; private final List<Parameter> sourceParameters;
private final List<Parameter> contextParameters; private final List<Parameter> contextParameters;
private final Parameter mappingTargetParameter;
/** /**
* Creates a new forged method with the given name. * Creates a new forged method with the given name.
* *
* @param name the (unique name) for this method * @param name the (unique name) for this method
* @param sourceType the source type * @param sourceType the source type
* @param targetType the target type. * @param returnType the return type.
* @param mapperConfiguration the mapper configuration * @param mapperConfiguration the mapper configuration
* @param positionHintElement element used to for reference to the position in the source file. * @param positionHintElement element used to for reference to the position in the source file.
* @param additionalParameters additional parameters to add to the forged method * @param additionalParameters additional parameters to add to the forged method
*/ */
public ForgedMethod(String name, Type sourceType, Type targetType, MapperConfiguration mapperConfiguration, public ForgedMethod(String name, Type sourceType, Type returnType, MapperConfiguration mapperConfiguration,
ExecutableElement positionHintElement, List<Parameter> additionalParameters) { ExecutableElement positionHintElement, List<Parameter> additionalParameters) {
this( name, sourceType, targetType, mapperConfiguration, positionHintElement, additionalParameters, null ); this( name, sourceType, returnType, mapperConfiguration, positionHintElement, additionalParameters, null );
} }
/** /**
@ -68,13 +69,13 @@ public class ForgedMethod implements Method {
* *
* @param name the (unique name) for this method * @param name the (unique name) for this method
* @param sourceType the source type * @param sourceType the source type
* @param targetType the target type. * @param returnType the return type.
* @param mapperConfiguration the mapper configuration * @param mapperConfiguration the mapper configuration
* @param positionHintElement element used to for reference to the position in the source file. * @param positionHintElement element used to for reference to the position in the source file.
* @param additionalParameters additional parameters to add to the forged method * @param additionalParameters additional parameters to add to the forged method
* @param history a parent forged method if this is a forged method within a forged method * @param history a parent forged method if this is a forged method within a forged method
*/ */
public ForgedMethod(String name, Type sourceType, Type targetType, MapperConfiguration mapperConfiguration, public ForgedMethod(String name, Type sourceType, Type returnType, MapperConfiguration mapperConfiguration,
ExecutableElement positionHintElement, List<Parameter> additionalParameters, ExecutableElement positionHintElement, List<Parameter> additionalParameters,
ForgedMethodHistory history) { ForgedMethodHistory history) {
String sourceParamName = Strings.decapitalize( sourceType.getName() ); String sourceParamName = Strings.decapitalize( sourceType.getName() );
@ -85,8 +86,9 @@ public class ForgedMethod implements Method {
this.parameters.addAll( additionalParameters ); this.parameters.addAll( additionalParameters );
this.sourceParameters = Parameter.getSourceParameters( parameters ); this.sourceParameters = Parameter.getSourceParameters( parameters );
this.contextParameters = Parameter.getContextParameters( parameters ); this.contextParameters = Parameter.getContextParameters( parameters );
this.mappingTargetParameter = Parameter.getMappingTargetParameter( parameters );
this.returnType = targetType; this.returnType = returnType;
this.thrownTypes = new ArrayList<Type>(); this.thrownTypes = new ArrayList<Type>();
this.name = Strings.sanitizeIdentifierName( name ); this.name = Strings.sanitizeIdentifierName( name );
this.mapperConfiguration = mapperConfiguration; this.mapperConfiguration = mapperConfiguration;
@ -109,6 +111,7 @@ public class ForgedMethod implements Method {
this.sourceParameters = Parameter.getSourceParameters( parameters ); this.sourceParameters = Parameter.getSourceParameters( parameters );
this.contextParameters = Parameter.getContextParameters( parameters ); this.contextParameters = Parameter.getContextParameters( parameters );
this.mappingTargetParameter = Parameter.getMappingTargetParameter( parameters );
this.name = name; this.name = name;
} }
@ -165,7 +168,7 @@ public class ForgedMethod implements Method {
@Override @Override
public Parameter getMappingTargetParameter() { public Parameter getMappingTargetParameter() {
return null; return mappingTargetParameter;
} }
@Override @Override
@ -203,7 +206,7 @@ public class ForgedMethod implements Method {
@Override @Override
public Type getResultType() { public Type getResultType() {
return returnType; return mappingTargetParameter != null ? mappingTargetParameter.getType() : returnType;
} }
@Override @Override
@ -303,4 +306,5 @@ public class ForgedMethod implements Method {
result = 31 * result + ( name != null ? name.hashCode() : 0 ); result = 31 * result + ( name != null ? name.hashCode() : 0 );
return result; return result;
} }
} }

View File

@ -228,8 +228,8 @@ public class SourceMethod implements Method {
this.sourceParameters = Parameter.getSourceParameters( parameters ); this.sourceParameters = Parameter.getSourceParameters( parameters );
this.contextParameters = Parameter.getContextParameters( parameters ); this.contextParameters = Parameter.getContextParameters( parameters );
this.mappingTargetParameter = determineMappingTargetParameter( parameters ); this.mappingTargetParameter = Parameter.getMappingTargetParameter( parameters );
this.targetTypeParameter = determineTargetTypeParameter( parameters ); this.targetTypeParameter = Parameter.getTargetTypeParameter( parameters );
this.isObjectFactory = determineIfIsObjectFactory( executable ); this.isObjectFactory = determineIfIsObjectFactory( executable );
this.typeUtils = typeUtils; this.typeUtils = typeUtils;
@ -248,26 +248,6 @@ public class SourceMethod implements Method {
&& ( hasFactoryAnnotation || hasNoSourceParameters ); && ( hasFactoryAnnotation || hasNoSourceParameters );
} }
private Parameter determineMappingTargetParameter(Iterable<Parameter> parameters) {
for ( Parameter parameter : parameters ) {
if ( parameter.isMappingTarget() ) {
return parameter;
}
}
return null;
}
private Parameter determineTargetTypeParameter(Iterable<Parameter> parameters) {
for ( Parameter parameter : parameters ) {
if ( parameter.isTargetType() ) {
return parameter;
}
}
return null;
}
@Override @Override
public Type getDeclaringMapper() { public Type getDeclaringMapper() {
return declaringMapper; return declaringMapper;

View File

@ -30,7 +30,8 @@ import static org.assertj.core.api.Assertions.assertThat;
Wheel.class, WheelDto.class, Wheel.class, WheelDto.class,
Roof.class, RoofDto.class, Roof.class, RoofDto.class,
UserDtoMapperClassic.class, UserDtoMapperClassic.class,
UserDtoMapperSmart.class UserDtoMapperSmart.class,
UserDtoUpdateMapperSmart.class
}) })
@RunWith(AnnotationProcessorTestRunner.class) @RunWith(AnnotationProcessorTestRunner.class)
public class NestedSimpleBeansMappingTest { public class NestedSimpleBeansMappingTest {
@ -46,9 +47,30 @@ public class NestedSimpleBeansMappingTest {
System.out.println( smartMapping ); System.out.println( smartMapping );
System.out.println( classicMapping ); System.out.println( classicMapping );
assertThat( smartMapping ).isNotNull(); assertThat( smartMapping ).isNotNull();
assertThat( smartMapping ).isEqualTo( classicMapping ); assertThat( smartMapping ).isEqualTo( classicMapping );
} }
@Test
public void shouldMapUpdateNestedBeans() {
User user = TestData.createUser();
user.getCar().setName( null );
// create a pre-exsiting smartMapping
UserDto smartMapping = new UserDto();
smartMapping.setCar( new CarDto() );
smartMapping.getCar().setName( "Toyota" );
// create a classic mapping and adapt expected result to Toyota
UserDto classicMapping = UserDtoMapperClassic.INSTANCE.userToUserDto( TestData.createUser() );
classicMapping.getCar().setName( "Toyota" );
// action
UserDtoUpdateMapperSmart.INSTANCE.userToUserDto( smartMapping, user );
// result
assertThat( smartMapping ).isNotNull();
assertThat( smartMapping ).isEqualTo( classicMapping );
}
} }

View File

@ -0,0 +1,33 @@
/**
* Copyright 2012-2017 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.nestedbeans;
import org.mapstruct.Mapper;
import org.mapstruct.MappingTarget;
import org.mapstruct.NullValueCheckStrategy;
import org.mapstruct.factory.Mappers;
@Mapper(nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS)
public interface UserDtoUpdateMapperSmart {
UserDtoUpdateMapperSmart INSTANCE = Mappers.getMapper( UserDtoUpdateMapperSmart.class );
void userToUserDto(@MappingTarget UserDto userDto, User user);
}