#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() ) {
return null;
}
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(
name,
sourceType,
targetType,
returnType,
method.getMapperConfiguration(),
method.getExecutable(),
method.getContextParameters(),
parameters,
getForgedMethodHistory( sourceRHS )
);

View File

@ -125,6 +125,15 @@ public class Parameter extends ModelElement {
ContextPrism.getInstanceOn( element ) != null );
}
public static Parameter forForgedMappingTarget(Type parameterType) {
return new Parameter(
"mappingTarget",
parameterType,
true,
false,
false);
}
/**
* @param parameters the parameters to filter
* @return the parameters from the given list that are considered 'source parameters'
@ -156,4 +165,24 @@ public class Parameter extends ModelElement {
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;
}
/**
* creates a void return type
*
* @return void type
*/
public Type createVoidType() {
return getType( typeUtils.getNoType( TypeKind.VOID ) );
}
/**
* Establishes the type bound:
* <ol>

View File

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

View File

@ -228,8 +228,8 @@ public class SourceMethod implements Method {
this.sourceParameters = Parameter.getSourceParameters( parameters );
this.contextParameters = Parameter.getContextParameters( parameters );
this.mappingTargetParameter = determineMappingTargetParameter( parameters );
this.targetTypeParameter = determineTargetTypeParameter( parameters );
this.mappingTargetParameter = Parameter.getMappingTargetParameter( parameters );
this.targetTypeParameter = Parameter.getTargetTypeParameter( parameters );
this.isObjectFactory = determineIfIsObjectFactory( executable );
this.typeUtils = typeUtils;
@ -248,26 +248,6 @@ public class SourceMethod implements Method {
&& ( 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
public Type getDeclaringMapper() {
return declaringMapper;

View File

@ -30,7 +30,8 @@ import static org.assertj.core.api.Assertions.assertThat;
Wheel.class, WheelDto.class,
Roof.class, RoofDto.class,
UserDtoMapperClassic.class,
UserDtoMapperSmart.class
UserDtoMapperSmart.class,
UserDtoUpdateMapperSmart.class
})
@RunWith(AnnotationProcessorTestRunner.class)
public class NestedSimpleBeansMappingTest {
@ -46,9 +47,30 @@ public class NestedSimpleBeansMappingTest {
System.out.println( smartMapping );
System.out.println( classicMapping );
assertThat( smartMapping ).isNotNull();
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);
}