diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java index 136828adf..f08e4a915 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/MethodMatcher.java @@ -248,10 +248,12 @@ public class MethodMatcher { // e.g. String method( List in ) Type.ResolvedPair resolved = mthdParType.resolveParameterToType( matchingType, aCandidateMethodType ); - if ( resolved == null ) { - // cannot find a candidate type, but should have since the typeFromCandidateMethod had parameters - // to be resolved - return !hasGenericTypeParameters( aCandidateMethodType ); + if ( resolved.getMatch() == null ) { + // we should be dealing with something containing a type parameter at this point. This is + // covered with the checks above. Therefore resolved itself cannot be null. + // If there is no match here, continue with the next candidate, perhaps there will a match with + // the next method type parameter + continue; } // resolved something at this point, a candidate can be fetched or created @@ -270,13 +272,10 @@ public class MethodMatcher { // it might be already set, but we just checked if its an equivalent type if ( typeVarCandidate.match == null ) { typeVarCandidate.match = resolved.getMatch(); - if ( typeVarCandidate.match == null) { - return false; - } typeVarCandidate.pairs.add( resolved ); } else if ( !areEquivalent( resolved.getMatch(), typeVarCandidate.match ) ) { - // type has been resolved twice, but with a different candidate (conflict) + // type has been resolved twice, but with a different candidate (conflict). Stop return false; } diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/multiple/MultipleTypeVarTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/multiple/MultipleTypeVarTest.java index b900fe21c..e928da4e7 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/multiple/MultipleTypeVarTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/multiple/MultipleTypeVarTest.java @@ -50,6 +50,22 @@ public class MultipleTypeVarTest { assertThat( target.getProp() ).containsExactly( entry( "test", 5L ) ); } + @ProcessorTest + @WithClasses( ReturnTypeHasMultipleTypeVarBothArgumentsFlippedGenericMapper.class ) + public void testGenericReturnTypeVarBothGenericArgumentsFlipped() { + + ReturnTypeHasMultipleTypeVarBothArgumentsFlippedGenericMapper.Pair pair + = new ReturnTypeHasMultipleTypeVarBothArgumentsFlippedGenericMapper.Pair( "test", 5L ); + ReturnTypeHasMultipleTypeVarBothArgumentsFlippedGenericMapper.Source src = + new ReturnTypeHasMultipleTypeVarBothArgumentsFlippedGenericMapper.Source( pair ); + ReturnTypeHasMultipleTypeVarBothArgumentsFlippedGenericMapper.Target target = + ReturnTypeHasMultipleTypeVarBothArgumentsFlippedGenericMapper.INSTANCE.toTarget( src ); + + assertThat( target ).isNotNull(); + assertThat( target.getProp() ).isNotNull(); + assertThat( target.getProp() ).containsExactly( entry( "test", 5L ) ); + } + @ProcessorTest @WithClasses( SourceTypeHasMultipleTypeVarBothGenericMapper.class ) public void testGenericSourceTypeVarBothGeneric() { diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/multiple/ReturnTypeHasMultipleTypeVarBothArgumentsFlippedGenericMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/multiple/ReturnTypeHasMultipleTypeVarBothArgumentsFlippedGenericMapper.java new file mode 100644 index 000000000..de7ce02e0 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/multiple/ReturnTypeHasMultipleTypeVarBothArgumentsFlippedGenericMapper.java @@ -0,0 +1,76 @@ +/* + * 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.selection.methodgenerics.multiple; + +import java.util.HashMap; +import java.util.Map; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + */ +@Mapper +public interface ReturnTypeHasMultipleTypeVarBothArgumentsFlippedGenericMapper { + + ReturnTypeHasMultipleTypeVarBothArgumentsFlippedGenericMapper INSTANCE = + Mappers.getMapper( ReturnTypeHasMultipleTypeVarBothArgumentsFlippedGenericMapper.class ); + + Target toTarget(Source source); + + default HashMap toMap( Pair entry) { + HashMap result = new HashMap<>( ); + result.put( entry.first, entry.second ); + return result; + } + + class Source { + + private Pair prop; + + public Source(Pair prop) { + this.prop = prop; + } + + public Pair getProp() { + return prop; + } + } + + class Target { + + private Map prop; + + public Map getProp() { + return prop; + } + + public Target setProp(Map prop) { + this.prop = prop; + return this; + } + } + + class Pair { + private final T first; + private final U second; + + public Pair(T first, U second) { + this.first = first; + this.second = second; + } + + public T getFirst() { + return first; + } + + public U getSecond() { + return second; + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/objectfactory/ObjectFactoryMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/objectfactory/ObjectFactoryMapper.java new file mode 100644 index 000000000..2ea183bfc --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/objectfactory/ObjectFactoryMapper.java @@ -0,0 +1,61 @@ +/* + * 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.selection.methodgenerics.objectfactory; + +import org.mapstruct.Mapper; +import org.mapstruct.ObjectFactory; +import org.mapstruct.TargetType; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface ObjectFactoryMapper { + + ObjectFactoryMapper INSTANCE = Mappers.getMapper( ObjectFactoryMapper.class ); + + TargetA toTarget(SourceA source); + + @ObjectFactory + default T createTarget(S source, @TargetType Class targetType) { + if ( source.isA() ) { + return (T) new TargetA(); + } + return (T) new TargetB(); + } + + abstract class Source { + public abstract boolean isA(); + } + + class SourceA extends Source { + @Override + public boolean isA() { + return true; + } + } + + class SourceB extends Source { + @Override + public boolean isA() { + return false; + } + } + + abstract class Target { + } + + class TargetA extends Target { + + private TargetA() { + } + } + + class TargetB extends Target { + + private TargetB() { + } + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/objectfactory/ObjectFactoryTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/objectfactory/ObjectFactoryTest.java new file mode 100644 index 000000000..21392710b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/objectfactory/ObjectFactoryTest.java @@ -0,0 +1,28 @@ +/* + * 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.selection.methodgenerics.objectfactory; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +public class ObjectFactoryTest { + + @IssueKey( "2463" ) + @ProcessorTest + @WithClasses( ObjectFactoryMapper.class ) + public void testSelectionOfFactoryMethod() { + + ObjectFactoryMapper.SourceA source = new ObjectFactoryMapper.SourceA(); + + ObjectFactoryMapper.Target target = ObjectFactoryMapper.INSTANCE.toTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target ).isInstanceOf( ObjectFactoryMapper.TargetA.class ); + } +}