mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#1001 auto mapping support for update methods
This commit is contained in:
parent
5cb80cbb97
commit
0188dcbdcc
@ -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 )
|
||||
);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user