diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java index 9361ddb3e..f0f6108d8 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceMethod.java @@ -76,6 +76,7 @@ public class SourceMethod implements Method { private List parameterNames; private List applicablePrototypeMethods; + private List applicableReversePrototypeMethods; private Boolean isBeanMapping; private Boolean isEnumMapping; @@ -312,11 +313,11 @@ public class SourceMethod implements Method { } public boolean reverses(SourceMethod method) { - return getDeclaringMapper() == null - && isAbstract() + return method.getDeclaringMapper() == null + && method.isAbstract() && getSourceParameters().size() == 1 && method.getSourceParameters().size() == 1 - && equals( first( getSourceParameters() ).getType(), method.getResultType() ) - && equals( getResultType(), first( method.getSourceParameters() ).getType() ); + && first( getSourceParameters() ).getType().isAssignableTo( method.getResultType() ) + && getResultType().isAssignableTo( first( method.getSourceParameters() ).getType() ); } public boolean isSame(SourceMethod method) { @@ -486,6 +487,20 @@ public class SourceMethod implements Method { return applicablePrototypeMethods; } + public List getApplicableReversePrototypeMethods() { + if ( applicableReversePrototypeMethods == null ) { + applicableReversePrototypeMethods = new ArrayList(); + + for ( SourceMethod prototype : prototypeMethods ) { + if ( reverses( prototype ) ) { + applicableReversePrototypeMethods.add( prototype ); + } + } + } + + return applicableReversePrototypeMethods; + } + private static boolean allParametersAreAssignable(List fromParams, List toParams) { if ( fromParams.size() == toParams.size() ) { Set unaccountedToParams = new HashSet( toParams ); diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java index 00d5a7f93..6c8e78a4d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/MapperCreationProcessor.java @@ -424,10 +424,15 @@ public class MapperCreationProcessor implements ModelElementProcessor applicablePrototypeMethods = method.getApplicablePrototypeMethods(); + List applicableReversePrototypeMethods = method.getApplicableReversePrototypeMethods(); MappingOptions inverseMappingOptions = - getInverseMappingOptions( availableMethods, method, initializingMethods, mapperConfig ); + getInverseMappingOptions( join( availableMethods, applicableReversePrototypeMethods ), + method, + initializingMethods, + mapperConfig ); + + List applicablePrototypeMethods = method.getApplicablePrototypeMethods(); MappingOptions templateMappingOptions = getTemplateMappingOptions( @@ -457,6 +462,21 @@ public class MapperCreationProcessor implements ModelElementProcessor 1 ) { + messager.printMessage( + method.getExecutable(), + Message.INHERITINVERSECONFIGURATION_MULTIPLE_PROTOTYPE_METHODS_MATCH, + Strings.join( applicablePrototypeMethods, ", " ) ); + } } mappingOptions.markAsFullyInitialized(); @@ -497,7 +517,7 @@ public class MapperCreationProcessor implements ModelElementProcessor candidates = new ArrayList(); for ( SourceMethod oneMethod : rawMethods ) { - if ( oneMethod.reverses( method ) ) { + if ( method.reverses( oneMethod ) ) { candidates.add( oneMethod ); } } @@ -559,7 +579,7 @@ public class MapperCreationProcessor implements ModelElementProcessor rawMethods, SourceMethod method, List initializingMethods, diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java index 3ac151f94..1da8d2706 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Message.java @@ -113,6 +113,7 @@ public enum Message { INHERITCONFIGURATION_DUPLICATE_MATCHES( "Given name \"%s\" matches several candidate methods: %s." ), INHERITCONFIGURATION_NO_NAME_MATCH( "Given name \"%s\" does not match the only candidate. Did you mean: \"%s\"." ), INHERITCONFIGURATION_MULTIPLE_PROTOTYPE_METHODS_MATCH( "More than one configuration prototype method is applicable. Use @InheritConfiguration to select one of them explicitly: %s." ), + INHERITINVERSECONFIGURATION_MULTIPLE_PROTOTYPE_METHODS_MATCH( "More than one configuration prototype method is applicable. Use @InheritInverseConfiguration to select one of them explicitly: %s." ), INHERITCONFIGURATION_CYCLE( "Cycle detected while evaluating inherited configurations. Inheritance path: %s" ), VALUEMAPPING_DUPLICATE_SOURCE( "Source value mapping: \"%s\" cannot be mapped more than once." ), diff --git a/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/CarMapperReverseWithExplicitInheritance.java b/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/CarMapperReverseWithExplicitInheritance.java new file mode 100644 index 000000000..0dec59790 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/CarMapperReverseWithExplicitInheritance.java @@ -0,0 +1,42 @@ +/** + * 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.inheritfromconfig; + +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingInheritanceStrategy; +import org.mapstruct.factory.Mappers; + +/** + * @author Andreas Gudian + */ +@Mapper( + config = AutoInheritedConfig.class, + mappingInheritanceStrategy = MappingInheritanceStrategy.EXPLICIT +) +public abstract class CarMapperReverseWithExplicitInheritance { + public static final CarMapperReverseWithExplicitInheritance INSTANCE = + Mappers.getMapper( CarMapperReverseWithExplicitInheritance.class ); + + @InheritInverseConfiguration(name = "baseDtoToEntity") + @Mapping( target = "colour", source = "color" ) + public abstract CarDto toCarDto(CarEntity entity); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/InheritFromConfigTest.java b/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/InheritFromConfigTest.java index bfa84a3f2..c3f6fd5bf 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/InheritFromConfigTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/inheritfromconfig/InheritFromConfigTest.java @@ -125,6 +125,21 @@ public class InheritFromConfigTest { assertThat( carDto.getId() ).isEqualTo( 42L ); } + @Test + @IssueKey( "1065" ) + @WithClasses({ CarMapperReverseWithExplicitInheritance.class } ) + public void explicitInheritedMappingIsAppliedInReverseDirectlyFromConfig() { + + CarEntity carEntity = new CarEntity(); + carEntity.setColor( "red" ); + carEntity.setPrimaryKey( 42L ); + + CarDto carDto = CarMapperReverseWithExplicitInheritance.INSTANCE.toCarDto( carEntity ); + + assertThat( carDto.getColour() ).isEqualTo( "red" ); + assertThat( carDto.getId() ).isEqualTo( 42L ); + } + @Test public void explicitInheritedMappingWithTwoLevelsIsOverriddenAtMethodLevel() { CarDto carDto = newTestDto(); diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/ArtistToChartEntryConfig.java b/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/ArtistToChartEntryConfig.java index aebc6616a..c4c1c6613 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/ArtistToChartEntryConfig.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/ArtistToChartEntryConfig.java @@ -37,5 +37,5 @@ public interface ArtistToChartEntryConfig { @Mapping(target = "artistName", source = "artist.name"), @Mapping(target = "chartName", ignore = true ) }) - BaseChartEntry mapForward( Song song ); + BaseChartEntry mapForwardConfig( Song song ); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/ArtistToChartEntryWithConfigReverse.java b/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/ArtistToChartEntryWithConfigReverse.java index ed6eadbe1..bd934dc10 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/ArtistToChartEntryWithConfigReverse.java +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedsourceproperties/ArtistToChartEntryWithConfigReverse.java @@ -44,7 +44,7 @@ public abstract class ArtistToChartEntryWithConfigReverse { }) abstract ChartEntryWithBase mapForward(Song song); - @InheritInverseConfiguration + @InheritInverseConfiguration( name = "mapForward" ) @Mapping(target = "positions", ignore = true) abstract Song mapReverse(ChartEntryWithBase ce); }