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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
@ -58,8 +59,10 @@ public class MethodReference extends ModelElement implements Assignment {
|
||||
private final Type definingType;
|
||||
private final List<ParameterBinding> parameterBindings;
|
||||
private final Parameter providingParameter;
|
||||
private final List<MethodReference> methodsToChain;
|
||||
private final boolean isStatic;
|
||||
private final boolean isConstructor;
|
||||
private final boolean isMethodChaining;
|
||||
|
||||
/**
|
||||
* Creates a new reference to the given method.
|
||||
@ -95,6 +98,8 @@ public class MethodReference extends ModelElement implements Assignment {
|
||||
this.isStatic = method.isStatic();
|
||||
this.name = method.getName();
|
||||
this.isConstructor = false;
|
||||
this.methodsToChain = Collections.emptyList();
|
||||
this.isMethodChaining = false;
|
||||
}
|
||||
|
||||
private MethodReference(BuiltInMethod method, ConversionContext contextParam) {
|
||||
@ -111,6 +116,8 @@ public class MethodReference extends ModelElement implements Assignment {
|
||||
this.isStatic = method.isStatic();
|
||||
this.name = method.getName();
|
||||
this.isConstructor = false;
|
||||
this.methodsToChain = Collections.emptyList();
|
||||
this.isMethodChaining = false;
|
||||
}
|
||||
|
||||
private MethodReference(String name, Type definingType, boolean isStatic) {
|
||||
@ -127,6 +134,8 @@ public class MethodReference extends ModelElement implements Assignment {
|
||||
this.providingParameter = null;
|
||||
this.isStatic = isStatic;
|
||||
this.isConstructor = false;
|
||||
this.methodsToChain = Collections.emptyList();
|
||||
this.isMethodChaining = false;
|
||||
}
|
||||
|
||||
private MethodReference(Type definingType, List<ParameterBinding> parameterBindings) {
|
||||
@ -142,6 +151,8 @@ public class MethodReference extends ModelElement implements Assignment {
|
||||
this.providingParameter = null;
|
||||
this.isStatic = false;
|
||||
this.isConstructor = true;
|
||||
this.methodsToChain = Collections.emptyList();
|
||||
this.isMethodChaining = false;
|
||||
|
||||
if ( parameterBindings.isEmpty() ) {
|
||||
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() {
|
||||
return declaringMapper;
|
||||
}
|
||||
@ -267,6 +296,12 @@ public class MethodReference extends ModelElement implements Assignment {
|
||||
if ( isStatic() ) {
|
||||
imported.add( definingType );
|
||||
}
|
||||
if ( isMethodChaining() ) {
|
||||
for ( MethodReference methodToChain : methodsToChain ) {
|
||||
imported.addAll( methodToChain.getImportTypes() );
|
||||
}
|
||||
}
|
||||
|
||||
return imported;
|
||||
}
|
||||
|
||||
@ -276,6 +311,12 @@ public class MethodReference extends ModelElement implements Assignment {
|
||||
if ( assignment != null ) {
|
||||
exceptions.addAll( assignment.getThrownTypes() );
|
||||
}
|
||||
if ( isMethodChaining() ) {
|
||||
for ( MethodReference methodToChain : methodsToChain ) {
|
||||
exceptions.addAll( methodToChain.getThrownTypes() );
|
||||
}
|
||||
|
||||
}
|
||||
return exceptions;
|
||||
}
|
||||
|
||||
@ -311,6 +352,14 @@ public class MethodReference extends ModelElement implements Assignment {
|
||||
return isConstructor;
|
||||
}
|
||||
|
||||
public boolean isMethodChaining() {
|
||||
return isMethodChaining;
|
||||
}
|
||||
|
||||
public List<MethodReference> getMethodsToChain() {
|
||||
return methodsToChain;
|
||||
}
|
||||
|
||||
public List<ParameterBinding> getParameterBindings() {
|
||||
return parameterBindings;
|
||||
}
|
||||
@ -385,6 +434,10 @@ public class MethodReference extends ModelElement implements Assignment {
|
||||
return new MethodReference( type, parameterBindings );
|
||||
}
|
||||
|
||||
public static MethodReference forMethodChaining(MethodReference... references) {
|
||||
return new MethodReference( references );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String mapper = declaringMapper != null ? declaringMapper.getType().getName() : "";
|
||||
|
@ -139,6 +139,10 @@ public class ObjectFactoryMethodResolver {
|
||||
}
|
||||
|
||||
public static MethodReference getBuilderFactoryMethod(Method method, BuilderType builder ) {
|
||||
return getBuilderFactoryMethod( method.getReturnType(), builder );
|
||||
}
|
||||
|
||||
public static MethodReference getBuilderFactoryMethod(Type typeToBuild, BuilderType builder ) {
|
||||
if ( builder == null ) {
|
||||
return null;
|
||||
}
|
||||
@ -149,7 +153,7 @@ public class ObjectFactoryMethodResolver {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( !builder.getBuildingType().isAssignableTo( method.getReturnType() ) ) {
|
||||
if ( !builder.getBuildingType().isAssignableTo( typeToBuild ) ) {
|
||||
//TODO print error message
|
||||
return null;
|
||||
}
|
||||
|
@ -419,6 +419,28 @@ public class PropertyMapping extends ModelElement {
|
||||
|
||||
Assignment factory = ObjectFactoryMethodResolver
|
||||
.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(
|
||||
rhs,
|
||||
method.getThrownTypes(),
|
||||
|
@ -18,6 +18,8 @@
|
||||
<@includeModel object=definingType/>.<@methodCall/>
|
||||
<#elseif constructor>
|
||||
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>
|
||||
<@methodCall/>
|
||||
</#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