mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#1997 Use builders to construct empty objects in update wrapper
This commit is contained in:
parent
464adc9143
commit
aed3ff5295
@ -6,6 +6,7 @@
|
|||||||
package org.mapstruct.ap.internal.model;
|
package org.mapstruct.ap.internal.model;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
@ -58,8 +59,10 @@ public class MethodReference extends ModelElement implements Assignment {
|
|||||||
private final Type definingType;
|
private final Type definingType;
|
||||||
private final List<ParameterBinding> parameterBindings;
|
private final List<ParameterBinding> parameterBindings;
|
||||||
private final Parameter providingParameter;
|
private final Parameter providingParameter;
|
||||||
|
private final List<MethodReference> methodsToChain;
|
||||||
private final boolean isStatic;
|
private final boolean isStatic;
|
||||||
private final boolean isConstructor;
|
private final boolean isConstructor;
|
||||||
|
private final boolean isMethodChaining;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new reference to the given method.
|
* Creates a new reference to the given method.
|
||||||
@ -95,6 +98,8 @@ public class MethodReference extends ModelElement implements Assignment {
|
|||||||
this.isStatic = method.isStatic();
|
this.isStatic = method.isStatic();
|
||||||
this.name = method.getName();
|
this.name = method.getName();
|
||||||
this.isConstructor = false;
|
this.isConstructor = false;
|
||||||
|
this.methodsToChain = Collections.emptyList();
|
||||||
|
this.isMethodChaining = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MethodReference(BuiltInMethod method, ConversionContext contextParam) {
|
private MethodReference(BuiltInMethod method, ConversionContext contextParam) {
|
||||||
@ -111,6 +116,8 @@ public class MethodReference extends ModelElement implements Assignment {
|
|||||||
this.isStatic = method.isStatic();
|
this.isStatic = method.isStatic();
|
||||||
this.name = method.getName();
|
this.name = method.getName();
|
||||||
this.isConstructor = false;
|
this.isConstructor = false;
|
||||||
|
this.methodsToChain = Collections.emptyList();
|
||||||
|
this.isMethodChaining = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MethodReference(String name, Type definingType, boolean isStatic) {
|
private MethodReference(String name, Type definingType, boolean isStatic) {
|
||||||
@ -127,6 +134,8 @@ public class MethodReference extends ModelElement implements Assignment {
|
|||||||
this.providingParameter = null;
|
this.providingParameter = null;
|
||||||
this.isStatic = isStatic;
|
this.isStatic = isStatic;
|
||||||
this.isConstructor = false;
|
this.isConstructor = false;
|
||||||
|
this.methodsToChain = Collections.emptyList();
|
||||||
|
this.isMethodChaining = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MethodReference(Type definingType, List<ParameterBinding> parameterBindings) {
|
private MethodReference(Type definingType, List<ParameterBinding> parameterBindings) {
|
||||||
@ -142,6 +151,8 @@ public class MethodReference extends ModelElement implements Assignment {
|
|||||||
this.providingParameter = null;
|
this.providingParameter = null;
|
||||||
this.isStatic = false;
|
this.isStatic = false;
|
||||||
this.isConstructor = true;
|
this.isConstructor = true;
|
||||||
|
this.methodsToChain = Collections.emptyList();
|
||||||
|
this.isMethodChaining = false;
|
||||||
|
|
||||||
if ( parameterBindings.isEmpty() ) {
|
if ( parameterBindings.isEmpty() ) {
|
||||||
this.importTypes = Collections.emptySet();
|
this.importTypes = Collections.emptySet();
|
||||||
@ -159,6 +170,24 @@ public class MethodReference extends ModelElement implements Assignment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private MethodReference(MethodReference... references) {
|
||||||
|
this.name = null;
|
||||||
|
this.definingType = null;
|
||||||
|
this.sourceParameters = Collections.emptyList();
|
||||||
|
this.returnType = null;
|
||||||
|
this.declaringMapper = null;
|
||||||
|
this.importTypes = Collections.emptySet();
|
||||||
|
this.thrownTypes = Collections.emptyList();
|
||||||
|
this.isUpdateMethod = false;
|
||||||
|
this.contextParam = null;
|
||||||
|
this.parameterBindings = null;
|
||||||
|
this.providingParameter = null;
|
||||||
|
this.isStatic = false;
|
||||||
|
this.isConstructor = false;
|
||||||
|
this.methodsToChain = Arrays.asList( references );
|
||||||
|
this.isMethodChaining = true;
|
||||||
|
}
|
||||||
|
|
||||||
public MapperReference getDeclaringMapper() {
|
public MapperReference getDeclaringMapper() {
|
||||||
return declaringMapper;
|
return declaringMapper;
|
||||||
}
|
}
|
||||||
@ -267,6 +296,12 @@ public class MethodReference extends ModelElement implements Assignment {
|
|||||||
if ( isStatic() ) {
|
if ( isStatic() ) {
|
||||||
imported.add( definingType );
|
imported.add( definingType );
|
||||||
}
|
}
|
||||||
|
if ( isMethodChaining() ) {
|
||||||
|
for ( MethodReference methodToChain : methodsToChain ) {
|
||||||
|
imported.addAll( methodToChain.getImportTypes() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return imported;
|
return imported;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,6 +311,12 @@ public class MethodReference extends ModelElement implements Assignment {
|
|||||||
if ( assignment != null ) {
|
if ( assignment != null ) {
|
||||||
exceptions.addAll( assignment.getThrownTypes() );
|
exceptions.addAll( assignment.getThrownTypes() );
|
||||||
}
|
}
|
||||||
|
if ( isMethodChaining() ) {
|
||||||
|
for ( MethodReference methodToChain : methodsToChain ) {
|
||||||
|
exceptions.addAll( methodToChain.getThrownTypes() );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
return exceptions;
|
return exceptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -311,6 +352,14 @@ public class MethodReference extends ModelElement implements Assignment {
|
|||||||
return isConstructor;
|
return isConstructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isMethodChaining() {
|
||||||
|
return isMethodChaining;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<MethodReference> getMethodsToChain() {
|
||||||
|
return methodsToChain;
|
||||||
|
}
|
||||||
|
|
||||||
public List<ParameterBinding> getParameterBindings() {
|
public List<ParameterBinding> getParameterBindings() {
|
||||||
return parameterBindings;
|
return parameterBindings;
|
||||||
}
|
}
|
||||||
@ -385,6 +434,10 @@ public class MethodReference extends ModelElement implements Assignment {
|
|||||||
return new MethodReference( type, parameterBindings );
|
return new MethodReference( type, parameterBindings );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static MethodReference forMethodChaining(MethodReference... references) {
|
||||||
|
return new MethodReference( references );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
String mapper = declaringMapper != null ? declaringMapper.getType().getName() : "";
|
String mapper = declaringMapper != null ? declaringMapper.getType().getName() : "";
|
||||||
|
@ -139,6 +139,10 @@ public class ObjectFactoryMethodResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static MethodReference getBuilderFactoryMethod(Method method, BuilderType builder ) {
|
public static MethodReference getBuilderFactoryMethod(Method method, BuilderType builder ) {
|
||||||
|
return getBuilderFactoryMethod( method.getReturnType(), builder );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MethodReference getBuilderFactoryMethod(Type typeToBuild, BuilderType builder ) {
|
||||||
if ( builder == null ) {
|
if ( builder == null ) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -149,7 +153,7 @@ public class ObjectFactoryMethodResolver {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !builder.getBuildingType().isAssignableTo( method.getReturnType() ) ) {
|
if ( !builder.getBuildingType().isAssignableTo( typeToBuild ) ) {
|
||||||
//TODO print error message
|
//TODO print error message
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -419,6 +419,28 @@ public class PropertyMapping extends ModelElement {
|
|||||||
|
|
||||||
Assignment factory = ObjectFactoryMethodResolver
|
Assignment factory = ObjectFactoryMethodResolver
|
||||||
.getFactoryMethod( method, targetType, SelectionParameters.forSourceRHS( rightHandSide ), ctx );
|
.getFactoryMethod( method, targetType, SelectionParameters.forSourceRHS( rightHandSide ), ctx );
|
||||||
|
|
||||||
|
if ( factory == null && targetBuilderType != null) {
|
||||||
|
// If there is no dedicated factory method and the target has a builder we will try to use that
|
||||||
|
MethodReference builderFactoryMethod = ObjectFactoryMethodResolver.getBuilderFactoryMethod(
|
||||||
|
targetType,
|
||||||
|
targetBuilderType
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( builderFactoryMethod != null ) {
|
||||||
|
|
||||||
|
MethodReference finisherMethod = BuilderFinisherMethodResolver.getBuilderFinisherMethod(
|
||||||
|
method,
|
||||||
|
targetBuilderType,
|
||||||
|
ctx
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( finisherMethod != null ) {
|
||||||
|
factory = MethodReference.forMethodChaining( builderFactoryMethod, finisherMethod );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
return new UpdateWrapper(
|
return new UpdateWrapper(
|
||||||
rhs,
|
rhs,
|
||||||
method.getThrownTypes(),
|
method.getThrownTypes(),
|
||||||
|
@ -18,6 +18,8 @@
|
|||||||
<@includeModel object=definingType/>.<@methodCall/>
|
<@includeModel object=definingType/>.<@methodCall/>
|
||||||
<#elseif constructor>
|
<#elseif constructor>
|
||||||
new <@includeModel object=definingType/><#if (parameterBindings?size > 0)>( <@arguments/> )<#else>()</#if>
|
new <@includeModel object=definingType/><#if (parameterBindings?size > 0)>( <@arguments/> )<#else>()</#if>
|
||||||
|
<#elseif methodChaining>
|
||||||
|
<#list methodsToChain as methodToChain><@includeModel object=methodToChain /><#if methodToChain_has_next>.</#if></#list>
|
||||||
<#else>
|
<#else>
|
||||||
<@methodCall/>
|
<@methodCall/>
|
||||||
</#if>
|
</#if>
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.bugs._1997;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
public class Car {
|
||||||
|
private String model;
|
||||||
|
|
||||||
|
private Car(Builder builder) {
|
||||||
|
this.model = builder.model;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getModel() {
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setModel(String model) {
|
||||||
|
this.model = model;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Builder builder() {
|
||||||
|
return new Builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
|
||||||
|
private String model;
|
||||||
|
|
||||||
|
public Builder model(String model) {
|
||||||
|
this.model = model;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Car build() {
|
||||||
|
return new Car( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.bugs._1997;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
public class CarDetail {
|
||||||
|
private String model;
|
||||||
|
|
||||||
|
private CarDetail(Builder builder) {
|
||||||
|
this.model = builder.model;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getModel() {
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setModel(String model) {
|
||||||
|
this.model = model;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Builder builder() {
|
||||||
|
return new Builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
|
||||||
|
private String model;
|
||||||
|
|
||||||
|
public Builder model(String model) {
|
||||||
|
this.model = model;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CarDetail build() {
|
||||||
|
return new CarDetail( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.bugs._1997;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
public class CarInsurance {
|
||||||
|
private CarDetail detail;
|
||||||
|
|
||||||
|
private CarInsurance(Builder builder) {
|
||||||
|
this.detail = builder.detail;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CarDetail getDetail() {
|
||||||
|
return detail;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDetail(CarDetail detail) {
|
||||||
|
this.detail = detail;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Builder builder() {
|
||||||
|
return new Builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
|
||||||
|
private CarDetail detail;
|
||||||
|
|
||||||
|
public Builder detail(CarDetail detail) {
|
||||||
|
this.detail = detail;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CarInsurance build() {
|
||||||
|
return new CarInsurance( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.bugs._1997;
|
||||||
|
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.Mapping;
|
||||||
|
import org.mapstruct.MappingTarget;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface CarInsuranceMapper {
|
||||||
|
|
||||||
|
CarInsuranceMapper INSTANCE = Mappers.getMapper( CarInsuranceMapper.class );
|
||||||
|
|
||||||
|
@Mapping(source = "model", target = "detail.model")
|
||||||
|
void update(Car source, @MappingTarget CarInsurance target);
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright MapStruct Authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*/
|
||||||
|
package org.mapstruct.ap.test.bugs._1997;
|
||||||
|
|
||||||
|
import org.mapstruct.ap.testutil.ProcessorTest;
|
||||||
|
import org.mapstruct.ap.testutil.WithClasses;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Filip Hrisafov
|
||||||
|
*/
|
||||||
|
@WithClasses({
|
||||||
|
Car.class,
|
||||||
|
CarDetail.class,
|
||||||
|
CarInsurance.class,
|
||||||
|
CarInsuranceMapper.class
|
||||||
|
})
|
||||||
|
class Issue1997Test {
|
||||||
|
|
||||||
|
@ProcessorTest
|
||||||
|
void shouldCorrectCreateIntermediateObjectsWithBuilder() {
|
||||||
|
Car source = Car.builder().model( "Model S" ).build();
|
||||||
|
CarInsurance target = CarInsurance.builder().build();
|
||||||
|
assertThat( target.getDetail() ).isNull();
|
||||||
|
|
||||||
|
CarInsuranceMapper.INSTANCE.update( source, target );
|
||||||
|
|
||||||
|
assertThat( target.getDetail() ).isNotNull();
|
||||||
|
assertThat( target.getDetail().getModel() ).isEqualTo( "Model S" );
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user