From 3004ea28c5ec4159e25a07c013777878bd7a1c67 Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Tue, 11 Jul 2017 21:53:38 +0200 Subject: [PATCH] #1131 Use SourceRHS source type for update methods factories If a SourceRHS is present then the source type of the SourceRHS and the MappingContext parameters are considered for the factory method selection, i.e. the other source parameters are ignored --- .../java/org/mapstruct/ObjectFactory.java | 2 +- .../mapstruct-reference-guide.asciidoc | 72 +++++++++++++++- .../conversion/ConversionProvider.java | 2 +- .../conversion/DateToStringConversion.java | 4 +- .../conversion/ReverseConversion.java | 2 +- .../internal/conversion/SimpleConversion.java | 2 +- .../internal/model/AbstractBaseBuilder.java | 3 +- .../model/AbstractMappingMethodBuilder.java | 3 +- .../model/CollectionAssignmentBuilder.java | 31 +++++-- .../model/ContainerMappingMethod.java | 2 +- .../model/ContainerMappingMethodBuilder.java | 5 +- .../internal/model/IterableMappingMethod.java | 2 +- .../ap/internal/model/MapMappingMethod.java | 5 +- .../internal/model/MappingBuilderContext.java | 3 +- .../ap/internal/model/MethodReference.java | 2 +- .../ap/internal/model/PropertyMapping.java | 11 ++- .../internal/model/StreamMappingMethod.java | 2 +- .../ap/internal/model/TypeConversion.java | 2 +- .../model/assignment/AdderWrapper.java | 1 + .../model/assignment/ArrayCopyWrapper.java | 1 + .../model/assignment/AssignmentWrapper.java | 1 + .../model/assignment/EnumConstantWrapper.java | 2 + ...nceSetterWrapperForCollectionsAndMaps.java | 1 + .../GetterWrapperForCollectionsAndMaps.java | 1 + .../assignment/Java8FunctionWrapper.java | 3 +- .../model/assignment/LocalVarWrapper.java | 1 + .../model/assignment/SetterWrapper.java | 1 + .../SetterWrapperForCollectionsAndMaps.java | 1 + ...perForCollectionsAndMapsWithNullCheck.java | 3 +- .../model/assignment/UpdateWrapper.java | 16 ++-- .../WrapperForCollectionsAndMaps.java | 1 + .../{assignment => common}/Assignment.java | 4 +- .../model/common/ParameterBinding.java | 29 +++++-- .../model/{ => common}/SourceRHS.java | 5 +- .../model/source/SelectionParameters.java | 31 +++++++ .../source/selector/SelectionCriteria.java | 8 ++ .../model/source/selector/TypeSelector.java | 20 ++++- .../creation/MappingResolverImpl.java | 6 +- .../ap/internal/model/MethodReference.ftl | 8 +- .../internal/model/{ => common}/SourceRHS.ftl | 2 +- .../ap/test/bugs/_1131/Issue1131Mapper.java | 60 +++++++++++++ .../_1131/Issue1131MapperWithContext.java | 79 +++++++++++++++++ .../ap/test/bugs/_1131/Issue1131Test.java | 86 +++++++++++++++++++ .../mapstruct/ap/test/bugs/_1131/Source.java | 58 +++++++++++++ .../mapstruct/ap/test/bugs/_1131/Target.java | 67 +++++++++++++++ 45 files changed, 590 insertions(+), 61 deletions(-) rename processor/src/main/java/org/mapstruct/ap/internal/model/{assignment => common}/Assignment.java (97%) rename processor/src/main/java/org/mapstruct/ap/internal/model/{ => common}/SourceRHS.java (95%) rename processor/src/main/resources/org/mapstruct/ap/internal/model/{ => common}/SourceRHS.ftl (97%) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_1131/Issue1131Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_1131/Issue1131MapperWithContext.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_1131/Issue1131Test.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_1131/Source.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_1131/Target.java diff --git a/core-common/src/main/java/org/mapstruct/ObjectFactory.java b/core-common/src/main/java/org/mapstruct/ObjectFactory.java index 13f7f246c..7319900b6 100644 --- a/core-common/src/main/java/org/mapstruct/ObjectFactory.java +++ b/core-common/src/main/java/org/mapstruct/ObjectFactory.java @@ -30,7 +30,7 @@ import java.lang.annotation.Target; * return type that is assignable to the required object type is present, then the factory method is used instead. *

* Factory methods can be defined without parameters, with an {@code @}{@link TargetType} parameter, a {@code @} - * {@link Context} parameter, or with a mapping methods source parameter. If any of those parameters are defined, then + * {@link Context} parameter, or with the mapping source parameter. If any of those parameters are defined, then * the mapping method that is supposed to use the factory method needs to be declared with an assignable result type, * assignable context parameter, and/or assignable source types. *

diff --git a/documentation/src/main/asciidoc/mapstruct-reference-guide.asciidoc b/documentation/src/main/asciidoc/mapstruct-reference-guide.asciidoc index c98afcda6..f3011a531 100644 --- a/documentation/src/main/asciidoc/mapstruct-reference-guide.asciidoc +++ b/documentation/src/main/asciidoc/mapstruct-reference-guide.asciidoc @@ -1579,7 +1579,7 @@ The mapping of enum to enum via the `@Mapping` annotation is *DEPRECATED*. It wi [[object-factories]] == Object factories -By default, the generated code for mapping one bean type into another will call the default constructor to instantiate the target type. +By default, the generated code for mapping one bean type into another or updating a bean will call the default constructor to instantiate the target type. Alternatively you can plug in custom object factories which will be invoked to obtain instances of the target type. One use case for this is JAXB which creates `ObjectFactory` classes for obtaining new instances of schema types. @@ -1613,7 +1613,7 @@ public class EntityFactory { @Mapper(uses= { DtoFactory.class, EntityFactory.class } ) public interface CarMapper { - OrderMapper INSTANCE = Mappers.getMapper( CarMapper.class ); + CarMapper INSTANCE = Mappers.getMapper( CarMapper.class ); CarDto carToCarDto(Car car); @@ -1659,6 +1659,74 @@ public class CarMapperImpl implements CarMapper { ---- ==== +.Custom object factories with update methods +==== +[source, java, linenums] +[subs="verbatim,attributes"] +---- +@Mapper(uses = { DtoFactory.class, EntityFactory.class, CarMapper.class } ) +public interface OwnerMapper { + + OwnerMapper INSTANCE = Mappers.getMapper( OwnerMapper.class ); + + void updateOwnerDto(Owner owner, @MappingTarget OwnerDto ownerDto); + + void updateOwner(OwnerDto ownerDto, @MappingTarget Owner owner); +} +---- +[source, java, linenums] +[subs="verbatim,attributes"] +---- +//GENERATED CODE +public class OwnerMapperImpl implements OwnerMapper { + + private final DtoFactory dtoFactory = new DtoFactory(); + + private final EntityFactory entityFactory = new EntityFactory(); + + private final OwnerMapper ownerMapper = Mappers.getMapper( OwnerMapper.class ); + + @Override + public void updateOwnerDto(Owner owner, @MappingTarget OwnerDto ownerDto) { + if ( owner == null ) { + return; + } + + if ( owner.getCar() != null ) { + if ( ownerDto.getCar() == null ) { + ownerDto.setCar( dtoFactory.createCarDto() ); + } + // update car within ownerDto + } + else { + ownerDto.setCar( null ); + } + + // updating other properties + } + + @Override + public void updateOwner(OwnerDto ownerDto, @MappingTarget Owner owner) { + if ( ownerDto == null ) { + return; + } + + if ( ownerDto.getCar() != null ) { + if ( owner.getCar() == null ) { + owner.setCar( entityFactory.createEntity( Car.class ) ); + } + // update car within owner + } + else { + owner.setCar( null ); + } + + // updating other properties + } +} +---- +==== + In addition, annotating a factory method with `@ObjectFactory` lets you gain access to the mapping sources. Source objects can be added as parameters in the same way as for mapping method. The `@ObjectFactory` annotation is necessary to let MapStruct know that the given method is only a factory method. diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionProvider.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionProvider.java index f0095c1e4..574f73dca 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionProvider.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionProvider.java @@ -20,7 +20,7 @@ package org.mapstruct.ap.internal.conversion; import java.util.List; import org.mapstruct.ap.internal.model.TypeConversion; -import org.mapstruct.ap.internal.model.assignment.Assignment; +import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.ConversionContext; import org.mapstruct.ap.internal.model.HelperMethod; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/DateToStringConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/DateToStringConversion.java index 9a27a9c79..f16a50095 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/DateToStringConversion.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/DateToStringConversion.java @@ -27,10 +27,10 @@ import java.util.Collections; import java.util.Date; import java.util.List; -import org.mapstruct.ap.internal.model.assignment.Assignment; -import org.mapstruct.ap.internal.model.common.ConversionContext; import org.mapstruct.ap.internal.model.HelperMethod; import org.mapstruct.ap.internal.model.TypeConversion; +import org.mapstruct.ap.internal.model.common.Assignment; +import org.mapstruct.ap.internal.model.common.ConversionContext; import org.mapstruct.ap.internal.model.common.Type; /** diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/ReverseConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/ReverseConversion.java index 282c87f02..3c914591e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/ReverseConversion.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/ReverseConversion.java @@ -20,7 +20,7 @@ package org.mapstruct.ap.internal.conversion; import java.util.Collections; import java.util.List; -import org.mapstruct.ap.internal.model.assignment.Assignment; +import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.ConversionContext; import org.mapstruct.ap.internal.model.HelperMethod; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/SimpleConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/SimpleConversion.java index 12846bad7..a69cbfbca 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/SimpleConversion.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/SimpleConversion.java @@ -23,7 +23,7 @@ import java.util.List; import java.util.Set; import org.mapstruct.ap.internal.model.TypeConversion; -import org.mapstruct.ap.internal.model.assignment.Assignment; +import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.ConversionContext; import org.mapstruct.ap.internal.model.HelperMethod; import org.mapstruct.ap.internal.model.common.Type; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractBaseBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractBaseBuilder.java index a7bc1b77b..9a2ec6b4a 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractBaseBuilder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractBaseBuilder.java @@ -18,8 +18,9 @@ */ package org.mapstruct.ap.internal.model; -import org.mapstruct.ap.internal.model.assignment.Assignment; +import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.ParameterBinding; +import org.mapstruct.ap.internal.model.common.SourceRHS; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.ForgedMethod; import org.mapstruct.ap.internal.model.source.MappingMethodUtils; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java index da40bf0dd..597ad093b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/AbstractMappingMethodBuilder.java @@ -19,7 +19,8 @@ package org.mapstruct.ap.internal.model; -import org.mapstruct.ap.internal.model.assignment.Assignment; +import org.mapstruct.ap.internal.model.common.Assignment; +import org.mapstruct.ap.internal.model.common.SourceRHS; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.ForgedMethod; import org.mapstruct.ap.internal.model.source.ForgedMethodHistory; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java index ea9f6dd6b..ee8e3e8ee 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/CollectionAssignmentBuilder.java @@ -18,14 +18,16 @@ */ package org.mapstruct.ap.internal.model; -import org.mapstruct.ap.internal.model.assignment.Assignment; import org.mapstruct.ap.internal.model.assignment.ExistingInstanceSetterWrapperForCollectionsAndMaps; import org.mapstruct.ap.internal.model.assignment.GetterWrapperForCollectionsAndMaps; import org.mapstruct.ap.internal.model.assignment.SetterWrapperForCollectionsAndMaps; import org.mapstruct.ap.internal.model.assignment.SetterWrapperForCollectionsAndMapsWithNullCheck; import org.mapstruct.ap.internal.model.assignment.UpdateWrapper; +import org.mapstruct.ap.internal.model.common.Assignment; +import org.mapstruct.ap.internal.model.common.SourceRHS; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.Method; +import org.mapstruct.ap.internal.model.source.SelectionParameters; import org.mapstruct.ap.internal.prism.CollectionMappingStrategyPrism; import org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism; import org.mapstruct.ap.internal.util.Message; @@ -65,7 +67,8 @@ public class CollectionAssignmentBuilder { private Type targetType; private String targetPropertyName; private PropertyMapping.TargetWriteAccessorType targetAccessorType; - private Assignment rhs; + private Assignment assignment; + private SourceRHS sourceRHS; public CollectionAssignmentBuilder mappingBuilderContext(MappingBuilderContext ctx) { this.ctx = ctx; @@ -97,13 +100,28 @@ public class CollectionAssignmentBuilder { return this; } - public CollectionAssignmentBuilder rightHandSide(Assignment rhs) { - this.rhs = rhs; + /** + * @param assignment the assignment that needs to be invoked + * + * @return this builder for chaining + */ + public CollectionAssignmentBuilder assignment(Assignment assignment) { + this.assignment = assignment; + return this; + } + + /** + * @param sourceRHS the source right hand side for getting the property for mapping + * + * @return this builder for chaining + */ + public CollectionAssignmentBuilder rightHandSide(SourceRHS sourceRHS) { + this.sourceRHS = sourceRHS; return this; } public Assignment build() { - Assignment result = rhs; + Assignment result = assignment; CollectionMappingStrategyPrism cms = method.getMapperConfiguration().getCollectionMappingStrategy(); boolean targetImmutable = cms == CollectionMappingStrategyPrism.TARGET_IMMUTABLE; @@ -121,7 +139,8 @@ public class CollectionAssignmentBuilder { targetPropertyName ); } - Assignment factoryMethod = ctx.getMappingResolver().getFactoryMethod( method, targetType, null ); + Assignment factoryMethod = ctx.getMappingResolver() + .getFactoryMethod( method, targetType, SelectionParameters.forSourceRHS( sourceRHS ) ); result = new UpdateWrapper( result, method.getThrownTypes(), diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ContainerMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ContainerMappingMethod.java index 984753509..1bb72088a 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ContainerMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ContainerMappingMethod.java @@ -22,7 +22,7 @@ import java.util.Collection; import java.util.List; import java.util.Set; -import org.mapstruct.ap.internal.model.assignment.Assignment; +import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.Method; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/ContainerMappingMethodBuilder.java b/processor/src/main/java/org/mapstruct/ap/internal/model/ContainerMappingMethodBuilder.java index 7a262fb46..5e7ce4995 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/ContainerMappingMethodBuilder.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/ContainerMappingMethodBuilder.java @@ -25,10 +25,11 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -import org.mapstruct.ap.internal.model.assignment.Assignment; +import org.mapstruct.ap.internal.model.common.Assignment; +import org.mapstruct.ap.internal.model.common.FormattingParameters; +import org.mapstruct.ap.internal.model.common.SourceRHS; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.ForgedMethod; -import org.mapstruct.ap.internal.model.common.FormattingParameters; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.SelectionParameters; import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/IterableMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/IterableMappingMethod.java index 2edafea3b..d9f2b41ca 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/IterableMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/IterableMappingMethod.java @@ -23,9 +23,9 @@ import static org.mapstruct.ap.internal.util.Collections.first; import java.util.Collection; import java.util.List; -import org.mapstruct.ap.internal.model.assignment.Assignment; import org.mapstruct.ap.internal.model.assignment.LocalVarWrapper; import org.mapstruct.ap.internal.model.assignment.SetterWrapper; +import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.SelectionParameters; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/MapMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/MapMappingMethod.java index d7733479d..1f02b7756 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/MapMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/MapMappingMethod.java @@ -24,12 +24,13 @@ import java.util.List; import java.util.Map; import java.util.Set; -import org.mapstruct.ap.internal.model.assignment.Assignment; import org.mapstruct.ap.internal.model.assignment.LocalVarWrapper; +import org.mapstruct.ap.internal.model.common.Assignment; +import org.mapstruct.ap.internal.model.common.FormattingParameters; import org.mapstruct.ap.internal.model.common.Parameter; +import org.mapstruct.ap.internal.model.common.SourceRHS; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.ForgedMethod; -import org.mapstruct.ap.internal.model.common.FormattingParameters; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.SelectionParameters; import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/MappingBuilderContext.java b/processor/src/main/java/org/mapstruct/ap/internal/model/MappingBuilderContext.java index e1e466a36..77bca9fcf 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/MappingBuilderContext.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/MappingBuilderContext.java @@ -28,8 +28,9 @@ import javax.lang.model.element.TypeElement; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; -import org.mapstruct.ap.internal.model.assignment.Assignment; +import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.FormattingParameters; +import org.mapstruct.ap.internal.model.common.SourceRHS; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.model.source.ForgedMethod; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/MethodReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/MethodReference.java index aee3fd30f..17c862962 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/MethodReference.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/MethodReference.java @@ -24,7 +24,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -import org.mapstruct.ap.internal.model.assignment.Assignment; +import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.ConversionContext; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.ParameterBinding; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java index db7fa579f..a4dc9f3df 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java @@ -18,7 +18,7 @@ */ package org.mapstruct.ap.internal.model; -import static org.mapstruct.ap.internal.model.assignment.Assignment.AssignmentType.DIRECT; +import static org.mapstruct.ap.internal.model.common.Assignment.AssignmentType.DIRECT; import static org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism.ALWAYS; import static org.mapstruct.ap.internal.util.Collections.first; import static org.mapstruct.ap.internal.util.Collections.last; @@ -34,14 +34,15 @@ import javax.lang.model.type.DeclaredType; import org.mapstruct.ap.internal.model.assignment.AdderWrapper; import org.mapstruct.ap.internal.model.assignment.ArrayCopyWrapper; -import org.mapstruct.ap.internal.model.assignment.Assignment; import org.mapstruct.ap.internal.model.assignment.EnumConstantWrapper; import org.mapstruct.ap.internal.model.assignment.GetterWrapperForCollectionsAndMaps; import org.mapstruct.ap.internal.model.assignment.SetterWrapper; import org.mapstruct.ap.internal.model.assignment.UpdateWrapper; +import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.FormattingParameters; import org.mapstruct.ap.internal.model.common.ModelElement; import org.mapstruct.ap.internal.model.common.Parameter; +import org.mapstruct.ap.internal.model.common.SourceRHS; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.ForgedMethod; import org.mapstruct.ap.internal.model.source.ForgedMethodHistory; @@ -393,7 +394,8 @@ public class PropertyMapping extends ModelElement { targetPropertyName ); } - Assignment factory = ctx.getMappingResolver().getFactoryMethod( method, targetType, null ); + Assignment factory = ctx.getMappingResolver() + .getFactoryMethod( method, targetType, SelectionParameters.forSourceRHS( rightHandSide ) ); return new UpdateWrapper( rhs, method.getThrownTypes(), factory, isFieldAssignment(), targetType, !rhs.isSourceReferenceParameter() ); } @@ -426,7 +428,8 @@ public class PropertyMapping extends ModelElement { .targetType( targetType ) .targetPropertyName( targetPropertyName ) .targetAccessorType( targetAccessorType ) - .rightHandSide( rhs ) + .rightHandSide( rightHandSide ) + .assignment( rhs ) .build(); } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/StreamMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/StreamMappingMethod.java index e4f41caef..6e2d067b8 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/StreamMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/StreamMappingMethod.java @@ -26,8 +26,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.stream.StreamSupport; -import org.mapstruct.ap.internal.model.assignment.Assignment; import org.mapstruct.ap.internal.model.assignment.Java8FunctionWrapper; +import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.SelectionParameters; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/TypeConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/model/TypeConversion.java index c5d62aff7..19c6a6f9e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/TypeConversion.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/TypeConversion.java @@ -22,7 +22,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -import org.mapstruct.ap.internal.model.assignment.Assignment; +import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.ModelElement; import org.mapstruct.ap.internal.model.common.Type; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/AdderWrapper.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/AdderWrapper.java index b41aa683c..3c7e03505 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/AdderWrapper.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/AdderWrapper.java @@ -23,6 +23,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.util.Nouns; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/ArrayCopyWrapper.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/ArrayCopyWrapper.java index c0354f053..2dfc5b6cd 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/ArrayCopyWrapper.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/ArrayCopyWrapper.java @@ -21,6 +21,7 @@ package org.mapstruct.ap.internal.model.assignment; import java.util.HashSet; import java.util.Set; +import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.Type; /** diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/AssignmentWrapper.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/AssignmentWrapper.java index 641c1e61e..6bcdb93ff 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/AssignmentWrapper.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/AssignmentWrapper.java @@ -21,6 +21,7 @@ package org.mapstruct.ap.internal.model.assignment; import java.util.List; import java.util.Set; +import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.ModelElement; import org.mapstruct.ap.internal.model.common.Type; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/EnumConstantWrapper.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/EnumConstantWrapper.java index 2d6bf8a63..80d9e403c 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/EnumConstantWrapper.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/EnumConstantWrapper.java @@ -20,6 +20,8 @@ package org.mapstruct.ap.internal.model.assignment; import java.util.HashSet; import java.util.Set; + +import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.Type; /** diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/ExistingInstanceSetterWrapperForCollectionsAndMaps.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/ExistingInstanceSetterWrapperForCollectionsAndMaps.java index b6b1aac7e..0e4ba1dc8 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/ExistingInstanceSetterWrapperForCollectionsAndMaps.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/ExistingInstanceSetterWrapperForCollectionsAndMaps.java @@ -20,6 +20,7 @@ package org.mapstruct.ap.internal.model.assignment; import java.util.List; +import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/GetterWrapperForCollectionsAndMaps.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/GetterWrapperForCollectionsAndMaps.java index 47dcae6e1..2d0c2429a 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/GetterWrapperForCollectionsAndMaps.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/GetterWrapperForCollectionsAndMaps.java @@ -22,6 +22,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.Type; /** diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/Java8FunctionWrapper.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/Java8FunctionWrapper.java index 74c111b11..6d4a00e5f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/Java8FunctionWrapper.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/Java8FunctionWrapper.java @@ -21,6 +21,7 @@ package org.mapstruct.ap.internal.model.assignment; import java.util.HashSet; import java.util.Set; +import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.Type; /** @@ -53,7 +54,7 @@ public class Java8FunctionWrapper extends AssignmentWrapper { /** * * @return {@code true} if the wrapped assignment is - * {@link org.mapstruct.ap.internal.model.assignment.Assignment.AssignmentType#DIRECT}, {@code false} otherwise + * {@link Assignment.AssignmentType#DIRECT}, {@code false} otherwise */ public boolean isDirectAssignment() { return getAssignment().getType() == AssignmentType.DIRECT; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/LocalVarWrapper.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/LocalVarWrapper.java index 88052ef3f..8b28b331f 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/LocalVarWrapper.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/LocalVarWrapper.java @@ -23,6 +23,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.Type; /** diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapper.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapper.java index c3c79afb3..97ccc25f0 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapper.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapper.java @@ -21,6 +21,7 @@ package org.mapstruct.ap.internal.model.assignment; import java.util.ArrayList; import java.util.List; +import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.prism.NullValueCheckStrategyPrism; diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMaps.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMaps.java index ffcb387c6..98b298f56 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMaps.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMaps.java @@ -20,6 +20,7 @@ package org.mapstruct.ap.internal.model.assignment; import java.util.List; +import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.Type; /** diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMapsWithNullCheck.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMapsWithNullCheck.java index dc6921fae..cd5b9eb07 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMapsWithNullCheck.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/SetterWrapperForCollectionsAndMapsWithNullCheck.java @@ -23,10 +23,11 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; -import static org.mapstruct.ap.internal.model.assignment.Assignment.AssignmentType.DIRECT; +import static org.mapstruct.ap.internal.model.common.Assignment.AssignmentType.DIRECT; /** * This wrapper handles the situation where an assignment is done via the setter and a null check is needed. diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/UpdateWrapper.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/UpdateWrapper.java index 8fb5b9e5f..e23f59899 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/UpdateWrapper.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/UpdateWrapper.java @@ -23,6 +23,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.Type; /** @@ -51,17 +52,17 @@ public class UpdateWrapper extends AssignmentWrapper { } private static Type determineImplType(Assignment factoryMethod, Type targetType) { + if ( factoryMethod != null ) { + //If we have factory method then we won't use the targetType + return null; + } if ( targetType.getImplementationType() != null ) { // it's probably a collection or something return targetType.getImplementationType(); } - if ( factoryMethod == null ) { - // no factory method means we create a new instance ourself and thus need to import the type - return targetType; - } - - return null; + // no factory method means we create a new instance ourself and thus need to import the type + return targetType; } @Override @@ -82,6 +83,9 @@ public class UpdateWrapper extends AssignmentWrapper { public Set getImportTypes() { Set imported = new HashSet(); imported.addAll( super.getImportTypes() ); + if ( factoryMethod != null ) { + imported.addAll( factoryMethod.getImportTypes() ); + } if ( targetImplementationType != null ) { imported.add( targetImplementationType ); imported.addAll( targetImplementationType.getTypeParameters() ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/WrapperForCollectionsAndMaps.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/WrapperForCollectionsAndMaps.java index b28a7de99..44411a444 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/WrapperForCollectionsAndMaps.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/WrapperForCollectionsAndMaps.java @@ -21,6 +21,7 @@ package org.mapstruct.ap.internal.model.assignment; import java.util.ArrayList; import java.util.List; +import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.Type; /** diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/Assignment.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Assignment.java similarity index 97% rename from processor/src/main/java/org/mapstruct/ap/internal/model/assignment/Assignment.java rename to processor/src/main/java/org/mapstruct/ap/internal/model/common/Assignment.java index e7e282dc3..a25fab69c 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/Assignment.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Assignment.java @@ -16,13 +16,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.mapstruct.ap.internal.model.assignment; +package org.mapstruct.ap.internal.model.common; import java.util.List; import java.util.Set; -import org.mapstruct.ap.internal.model.common.Type; - /** * Assignment represents all kind of manners a source can be assigned to a target. * diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/ParameterBinding.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/ParameterBinding.java index fcf57d26d..9cfdfcaec 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/ParameterBinding.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/ParameterBinding.java @@ -35,14 +35,16 @@ public class ParameterBinding { private final boolean targetType; private final boolean mappingTarget; private final boolean mappingContext; + private final SourceRHS sourceRHS; private ParameterBinding(Type parameterType, String variableName, boolean mappingTarget, boolean targetType, - boolean mappingContext) { + boolean mappingContext, SourceRHS sourceRHS) { this.type = parameterType; this.variableName = variableName; this.targetType = targetType; this.mappingTarget = mappingTarget; this.mappingContext = mappingContext; + this.sourceRHS = sourceRHS; } /** @@ -80,11 +82,22 @@ public class ParameterBinding { return type; } + /** + * @return the sourceRHS that this parameter is bound to + */ + public SourceRHS getSourceRHS() { + return sourceRHS; + } + public Set getImportTypes() { if ( targetType ) { return type.getImportTypes(); } + if ( sourceRHS != null ) { + return sourceRHS.getImportTypes(); + } + return Collections.emptySet(); } @@ -98,7 +111,9 @@ public class ParameterBinding { parameter.getName(), parameter.isMappingTarget(), parameter.isTargetType(), - parameter.isMappingContext() ); + parameter.isMappingContext(), + null + ); } public static List fromParameters(List parameters) { @@ -114,7 +129,7 @@ public class ParameterBinding { * @return a parameter binding representing a target type parameter */ public static ParameterBinding forTargetTypeBinding(Type classTypeOf) { - return new ParameterBinding( classTypeOf, null, false, true, false ); + return new ParameterBinding( classTypeOf, null, false, true, false, null ); } /** @@ -122,7 +137,7 @@ public class ParameterBinding { * @return a parameter binding representing a mapping target parameter */ public static ParameterBinding forMappingTargetBinding(Type resultType) { - return new ParameterBinding( resultType, null, true, false, false ); + return new ParameterBinding( resultType, null, true, false, false, null ); } /** @@ -130,6 +145,10 @@ public class ParameterBinding { * @return a parameter binding representing a mapping source type */ public static ParameterBinding forSourceTypeBinding(Type sourceType) { - return new ParameterBinding( sourceType, null, false, false, false ); + return new ParameterBinding( sourceType, null, false, false, false, null ); + } + + public static ParameterBinding fromSourceRHS(SourceRHS sourceRHS) { + return new ParameterBinding( sourceRHS.getSourceType(), null, false, false, false, sourceRHS ); } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/SourceRHS.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/SourceRHS.java similarity index 95% rename from processor/src/main/java/org/mapstruct/ap/internal/model/SourceRHS.java rename to processor/src/main/java/org/mapstruct/ap/internal/model/common/SourceRHS.java index cdcd1d2a2..90493453b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/SourceRHS.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/SourceRHS.java @@ -16,15 +16,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.mapstruct.ap.internal.model; +package org.mapstruct.ap.internal.model.common; import java.util.Collections; import java.util.List; import java.util.Set; -import org.mapstruct.ap.internal.model.assignment.Assignment; -import org.mapstruct.ap.internal.model.common.ModelElement; -import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.util.Strings; /** diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SelectionParameters.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SelectionParameters.java index 9c3aa84c3..de95e77b3 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SelectionParameters.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SelectionParameters.java @@ -18,10 +18,13 @@ */ package org.mapstruct.ap.internal.model.source; +import java.util.Collections; import java.util.List; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Types; +import org.mapstruct.ap.internal.model.common.SourceRHS; + /** * Holding parameters common to the selection process, common to IterableMapping, BeanMapping, PropertyMapping and * MapMapping @@ -34,13 +37,20 @@ public class SelectionParameters { private final List qualifyingNames; private final TypeMirror resultType; private final Types typeUtils; + private final SourceRHS sourceRHS; public SelectionParameters(List qualifiers, List qualifyingNames, TypeMirror resultType, Types typeUtils) { + this( qualifiers, qualifyingNames, resultType, typeUtils, null ); + } + + private SelectionParameters(List qualifiers, List qualifyingNames, TypeMirror resultType, + Types typeUtils, SourceRHS sourceRHS) { this.qualifiers = qualifiers; this.qualifyingNames = qualifyingNames; this.resultType = resultType; this.typeUtils = typeUtils; + this.sourceRHS = sourceRHS; } /** @@ -68,6 +78,13 @@ public class SelectionParameters { return resultType; } + /** + * @return sourceRHS used for further selection of an appropriate factory method + */ + public SourceRHS getSourceRHS() { + return sourceRHS; + } + @Override public int hashCode() { int hash = 3; @@ -97,6 +114,10 @@ public class SelectionParameters { return false; } + if ( !equals( this.sourceRHS, other.sourceRHS ) ) { + return false; + } + return equals( this.resultType, other.resultType ); } @@ -133,4 +154,14 @@ public class SelectionParameters { return mirror2 != null && typeUtils.isSameType( mirror1, mirror2 ); } } + + public static SelectionParameters forSourceRHS(SourceRHS sourceRHS) { + return new SelectionParameters( + Collections.emptyList(), + Collections.emptyList(), + null, + null, + sourceRHS + ); + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionCriteria.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionCriteria.java index 522f422c6..9856dd928 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionCriteria.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/SelectionCriteria.java @@ -23,6 +23,7 @@ import java.util.List; import javax.lang.model.type.TypeMirror; +import org.mapstruct.ap.internal.model.common.SourceRHS; import org.mapstruct.ap.internal.model.source.SelectionParameters; /** @@ -36,6 +37,7 @@ public class SelectionCriteria { private final List qualifiedByNames = new ArrayList(); private final String targetPropertyName; private final TypeMirror qualifyingResultType; + private final SourceRHS sourceRHS; private boolean preferUpdateMapping; private final boolean objectFactoryRequired; private final boolean lifecycleCallbackRequired; @@ -47,9 +49,11 @@ public class SelectionCriteria { qualifiers.addAll( selectionParameters.getQualifiers() ); qualifiedByNames.addAll( selectionParameters.getQualifyingNames() ); qualifyingResultType = selectionParameters.getResultType(); + sourceRHS = selectionParameters.getSourceRHS(); } else { this.qualifyingResultType = null; + sourceRHS = null; } this.targetPropertyName = targetPropertyName; this.preferUpdateMapping = preferUpdateMapping; @@ -91,6 +95,10 @@ public class SelectionCriteria { return preferUpdateMapping; } + public SourceRHS getSourceRHS() { + return sourceRHS; + } + public void setPreferUpdateMapping(boolean preferUpdateMapping) { this.preferUpdateMapping = preferUpdateMapping; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TypeSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TypeSelector.java index d2258239d..ce977f566 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TypeSelector.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/TypeSelector.java @@ -25,6 +25,7 @@ import java.util.List; import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.ParameterBinding; +import org.mapstruct.ap.internal.model.common.SourceRHS; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.model.source.Method; @@ -59,7 +60,11 @@ public class TypeSelector implements MethodSelector { List availableBindings; if ( sourceTypes.isEmpty() ) { // if no source types are given, we have a factory or lifecycle method - availableBindings = getAvailableParameterBindingsFromMethod( mappingMethod, targetType ); + availableBindings = getAvailableParameterBindingsFromMethod( + mappingMethod, + targetType, + criteria.getSourceRHS() + ); } else { availableBindings = getAvailableParameterBindingsFromSourceTypes( sourceTypes, targetType, mappingMethod ); @@ -81,11 +86,18 @@ public class TypeSelector implements MethodSelector { return result; } - private List getAvailableParameterBindingsFromMethod(Method method, Type targetType) { - List availableParams = new ArrayList( method.getParameters().size() + 2 ); + private List getAvailableParameterBindingsFromMethod(Method method, Type targetType, + SourceRHS sourceRHS) { + List availableParams = new ArrayList( method.getParameters().size() + 3 ); - availableParams.addAll( ParameterBinding.fromParameters( method.getParameters() ) ); addMappingTargetAndTargetTypeBindings( availableParams, targetType ); + if ( sourceRHS != null ) { + availableParams.addAll( ParameterBinding.fromParameters( method.getContextParameters() ) ); + availableParams.add( ParameterBinding.fromSourceRHS( sourceRHS ) ); + } + else { + availableParams.addAll( ParameterBinding.fromParameters( method.getParameters() ) ); + } return availableParams; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java index e86e7c7fe..19d23b9d7 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/creation/MappingResolverImpl.java @@ -42,14 +42,14 @@ import org.mapstruct.ap.internal.model.HelperMethod; import org.mapstruct.ap.internal.model.MapperReference; import org.mapstruct.ap.internal.model.MappingBuilderContext.MappingResolver; import org.mapstruct.ap.internal.model.MethodReference; -import org.mapstruct.ap.internal.model.SourceRHS; import org.mapstruct.ap.internal.model.VirtualMappingMethod; -import org.mapstruct.ap.internal.model.assignment.Assignment; +import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.ConversionContext; import org.mapstruct.ap.internal.model.common.DefaultConversionContext; +import org.mapstruct.ap.internal.model.common.FormattingParameters; +import org.mapstruct.ap.internal.model.common.SourceRHS; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; -import org.mapstruct.ap.internal.model.common.FormattingParameters; import org.mapstruct.ap.internal.model.source.Method; import org.mapstruct.ap.internal.model.source.SelectionParameters; import org.mapstruct.ap.internal.model.source.builtin.BuiltInMappingMethods; diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReference.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReference.ftl index 7b603d784..8f4a31ddc 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReference.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/MethodReference.ftl @@ -53,8 +53,10 @@ ${ext.targetBeanName}<#if ext.targetReadAccessorName??>.${ext.targetReadAccessorName}<#t> <#elseif param.mappingContext> ${param.variableName}<#t> + <#elseif param.sourceRHS??> + <@_assignment assignmentToUse=param.sourceRHS/><#t> <#elseif assignment??> - <@_assignment/><#t> + <@_assignment assignmentToUse=assignment/><#t> <#else> ${param.variableName}<#t> @@ -67,8 +69,8 @@ macro: assignment purpose: note: takes its targetyType from the singleSourceParameterType --> -<#macro _assignment> - <@includeModel object=assignment +<#macro _assignment assignmentToUse> + <@includeModel object=assignmentToUse targetBeanName=ext.targetBeanName existingInstanceMapping=ext.existingInstanceMapping targetReadAccessorName=ext.targetReadAccessorName diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/SourceRHS.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/common/SourceRHS.ftl similarity index 97% rename from processor/src/main/resources/org/mapstruct/ap/internal/model/SourceRHS.ftl rename to processor/src/main/resources/org/mapstruct/ap/internal/model/common/SourceRHS.ftl index 4b3df1fd1..1e22080cf 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/SourceRHS.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/common/SourceRHS.ftl @@ -1,4 +1,4 @@ -<#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.SourceRHS" --> +<#-- @ftlvariable name="" type="org.mapstruct.ap.internal.model.common.SourceRHS" --> <#-- Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/) diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1131/Issue1131Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1131/Issue1131Mapper.java new file mode 100644 index 000000000..5059ffe6c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1131/Issue1131Mapper.java @@ -0,0 +1,60 @@ +/** + * 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.bugs._1131; + +import java.util.ArrayList; +import java.util.List; + +import org.mapstruct.Mapper; +import org.mapstruct.MappingTarget; +import org.mapstruct.ObjectFactory; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public abstract class Issue1131Mapper { + public static final Issue1131Mapper INSTANCE = Mappers.getMapper( Issue1131Mapper.class ); + + public static final List CALLED_METHODS = new ArrayList(); + + public abstract void merge(Source source, @MappingTarget Target target); + + public abstract void mergeNested(List source, @MappingTarget List target); + + @ObjectFactory + protected Target.Nested create(Source.Nested source) { + CALLED_METHODS.add( "create(Source.Nested)" ); + return new Target.Nested( "from object factory" ); + } + + @ObjectFactory + protected Target.Nested createWithSource(Source source) { + throw new IllegalArgumentException( "Should not use create with source" ); + } + + @ObjectFactory + protected List createWithSourceList(List source) { + CALLED_METHODS.add( "create(List)" ); + List result = new ArrayList(); + result.add( new Target.Nested( "from createWithSourceList" ) ); + return result; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1131/Issue1131MapperWithContext.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1131/Issue1131MapperWithContext.java new file mode 100644 index 000000000..c75a96775 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1131/Issue1131MapperWithContext.java @@ -0,0 +1,79 @@ +/** + * 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.bugs._1131; + +import java.util.ArrayList; +import java.util.List; + +import org.mapstruct.Context; +import org.mapstruct.Mapper; +import org.mapstruct.MappingTarget; +import org.mapstruct.ObjectFactory; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public abstract class Issue1131MapperWithContext { + public static final Issue1131MapperWithContext INSTANCE = Mappers.getMapper( Issue1131MapperWithContext.class ); + + public static class MappingContext { + private final List calledMethods = new ArrayList(); + + public Target.Nested create(Source.Nested source) { + calledMethods.add( "create(Source.Nested)" ); + return new Target.Nested( "from within @Context" ); + } + + public List create(List source) { + calledMethods.add( "create(List)" ); + if ( source == null ) { + return new ArrayList(); + } + else { + return new ArrayList( source.size() ); + } + } + + public List getCalledMethods() { + return calledMethods; + } + } + + public abstract void merge(Source source, @MappingTarget Target target, @Context MappingContext context); + + public abstract void merge(List source, @MappingTarget List target, + @Context MappingContext context); + + @ObjectFactory + protected Target.Nested create(Source.Nested source, @Context MappingContext context) { + return context.create( source ); + } + + @ObjectFactory + protected Target.Nested createWithSource(Source source, @Context MappingContext context) { + throw new IllegalArgumentException( "Should not use create with source" ); + } + + @ObjectFactory + protected List createWithSourceList(List source, @Context MappingContext context) { + return context.create( source ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1131/Issue1131Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1131/Issue1131Test.java new file mode 100644 index 000000000..3f1d16776 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1131/Issue1131Test.java @@ -0,0 +1,86 @@ +/** + * 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.bugs._1131; + +import java.util.ArrayList; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +@RunWith(AnnotationProcessorTestRunner.class) +@IssueKey("1131") +@WithClasses({ + Issue1131Mapper.class, + Issue1131MapperWithContext.class, + Source.class, + Target.class +}) +public class Issue1131Test { + + @Test + public void shouldUseCreateWithSourceNested() { + + Source source = new Source(); + source.setNested( new Source.Nested() ); + source.getNested().setProperty( "something" ); + source.setMoreNested( new ArrayList() ); + + Target target = new Target(); + + Issue1131Mapper.INSTANCE.merge( source, target ); + + assertThat( target.getNested() ).isNotNull(); + assertThat( target.getNested().getProperty() ).isEqualTo( "something" ); + assertThat( target.getNested().getInternal() ).isEqualTo( "from object factory" ); + assertThat( Issue1131Mapper.CALLED_METHODS ).containsExactly( + "create(Source.Nested)", + "create(List)" + ); + } + + @Test + public void shouldUseContextObjectFactory() { + + Source source = new Source(); + source.setNested( new Source.Nested() ); + source.getNested().setProperty( "something" ); + source.setMoreNested( new ArrayList() ); + + Target target = new Target(); + + Issue1131MapperWithContext.MappingContext context = new Issue1131MapperWithContext.MappingContext(); + Issue1131MapperWithContext.INSTANCE.merge( source, target, context ); + + assertThat( target.getNested() ).isNotNull(); + assertThat( target.getNested().getProperty() ).isEqualTo( "something" ); + assertThat( target.getNested().getInternal() ).isEqualTo( "from within @Context" ); + assertThat( context.getCalledMethods() ).containsExactly( + "create(Source.Nested)", + "create(List)" + ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1131/Source.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1131/Source.java new file mode 100644 index 000000000..fef5f8d43 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1131/Source.java @@ -0,0 +1,58 @@ +/** + * 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.bugs._1131; + +import java.util.List; + +/** + * @author Filip Hrisafov + */ +public class Source { + + public static class Nested { + private String property; + + public String getProperty() { + return property; + } + + public void setProperty(String property) { + this.property = property; + } + } + + private Nested nested; + private List moreNested; + + public Nested getNested() { + return nested; + } + + public void setNested(Nested nested) { + this.nested = nested; + } + + public List getMoreNested() { + return moreNested; + } + + public void setMoreNested(List moreNested) { + this.moreNested = moreNested; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_1131/Target.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1131/Target.java new file mode 100644 index 000000000..d97617950 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_1131/Target.java @@ -0,0 +1,67 @@ +/** + * 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.bugs._1131; + +import java.util.List; + +/** + * @author Filip Hrisafov + */ +public class Target { + + public static class Nested { + private final String internal; + private String property; + + public Nested(String internal) { + this.internal = internal; + } + + public String getInternal() { + return internal; + } + + public String getProperty() { + return property; + } + + public void setProperty(String property) { + this.property = property; + } + } + + private Nested nested; + private List moreNested; + + public Nested getNested() { + return nested; + } + + public void setNested(Nested nested) { + this.nested = nested; + } + + public List getMoreNested() { + return moreNested; + } + + public void setMoreNested(List moreNested) { + this.moreNested = moreNested; + } +}