diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java
index e017d1b00..e64b20799 100644
--- a/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java
+++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/FullFeatureCompilationExclusionCliEnhancer.java
@@ -32,6 +32,10 @@ public final class FullFeatureCompilationExclusionCliEnhancer implements Process
additionalExcludes.add( "org/mapstruct/ap/test/injectionstrategy/cdi/**/*.java" );
additionalExcludes.add( "org/mapstruct/ap/test/injectionstrategy/jakarta_cdi/**/*.java" );
additionalExcludes.add( "org/mapstruct/ap/test/annotatewith/deprecated/jdk11/*.java" );
+ if ( processorType == ProcessorTest.ProcessorType.ECLIPSE_JDT ) {
+ additionalExcludes.add(
+ "org/mapstruct/ap/test/selection/methodgenerics/wildcards/LifecycleIntersectionMapper.java" );
+ }
break;
case JAVA_9:
// TODO find out why this fails:
diff --git a/integrationtest/src/test/resources/fullFeatureTest/pom.xml b/integrationtest/src/test/resources/fullFeatureTest/pom.xml
index 8a62f4858..9c17f6c7d 100644
--- a/integrationtest/src/test/resources/fullFeatureTest/pom.xml
+++ b/integrationtest/src/test/resources/fullFeatureTest/pom.xml
@@ -26,6 +26,7 @@
x
x
x
+ x
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java
index fd7f3d690..6d86aad45 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java
@@ -28,6 +28,7 @@ import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.IntersectionType;
import javax.lang.model.type.PrimitiveType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
@@ -114,6 +115,7 @@ public class Type extends ModelElement implements Comparable {
private List alternativeTargetAccessors = null;
private Type boundingBase = null;
+ private List boundTypes = null;
private Type boxedEquivalent = null;
@@ -354,6 +356,10 @@ public class Type extends ModelElement implements Comparable {
return (typeMirror.getKind() == TypeKind.TYPEVAR);
}
+ public boolean isIntersection() {
+ return typeMirror.getKind() == TypeKind.INTERSECTION;
+ }
+
public boolean isJavaLangType() {
return packageName != null && packageName.startsWith( "java." );
}
@@ -1264,6 +1270,29 @@ public class Type extends ModelElement implements Comparable {
return boundingBase;
}
+ public List getTypeBounds() {
+ if ( this.boundTypes != null ) {
+ return boundTypes;
+ }
+ Type bound = getTypeBound();
+ if ( bound == null ) {
+ this.boundTypes = Collections.emptyList();
+ }
+ else if ( !bound.isIntersection() ) {
+ this.boundTypes = Collections.singletonList( bound );
+ }
+ else {
+ List extends TypeMirror> bounds = ( (IntersectionType) bound.typeMirror ).getBounds();
+ this.boundTypes = new ArrayList<>( bounds.size() );
+ for ( TypeMirror mirror : bounds ) {
+ boundTypes.add( typeFactory.getType( mirror ) );
+ }
+ }
+
+ return this.boundTypes;
+
+ }
+
public boolean hasAccessibleConstructor() {
if ( hasAccessibleConstructor == null ) {
hasAccessibleConstructor = false;
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 42c26f09a..3d8bfea13 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
@@ -325,8 +325,7 @@ public class MethodMatcher {
*/
private boolean candidatesWithinBounds(Map methodParCandidates ) {
for ( Map.Entry entry : methodParCandidates.entrySet() ) {
- Type bound = entry.getKey().getTypeBound();
- if ( bound != null ) {
+ for ( Type bound : entry.getKey().getTypeBounds() ) {
for ( Type.ResolvedPair pair : entry.getValue().pairs ) {
if ( entry.getKey().hasUpperBound() ) {
if ( !pair.getMatch().asRawType().isAssignableTo( bound.asRawType() ) ) {
diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/wildcards/LifecycleIntersectionMapper.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/wildcards/LifecycleIntersectionMapper.java
new file mode 100644
index 000000000..c466ee0d9
--- /dev/null
+++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/wildcards/LifecycleIntersectionMapper.java
@@ -0,0 +1,96 @@
+/*
+ * 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.wildcards;
+
+import org.mapstruct.AfterMapping;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.MappingTarget;
+import org.mapstruct.factory.Mappers;
+
+/**
+ * @author Filip Hrisafov
+ */
+@Mapper
+public interface LifecycleIntersectionMapper {
+
+ LifecycleIntersectionMapper INSTANCE = Mappers.getMapper( LifecycleIntersectionMapper.class );
+
+ @Mapping(target = "realm", ignore = true)
+ RealmTarget mapRealm(String source);
+
+ @Mapping(target = "uniqueRealm", ignore = true)
+ UniqueRealmTarget mapUniqueRealm(String source);
+
+ @Mapping(target = "realm", ignore = true)
+ @Mapping(target = "uniqueRealm", ignore = true)
+ BothRealmsTarget mapBothRealms(String source);
+
+ @AfterMapping
+ default void afterMapping(String source, @MappingTarget T target) {
+ target.setRealm( "realm_" + source );
+ target.setUniqueRealm( "uniqueRealm_" + source );
+ }
+
+ interface RealmObject {
+ void setRealm(String realm);
+ }
+
+ interface UniqueRealmObject {
+ void setUniqueRealm(String realm);
+ }
+
+ class RealmTarget implements RealmObject {
+
+ protected String realm;
+
+ public String getRealm() {
+ return realm;
+ }
+
+ @Override
+ public void setRealm(String realm) {
+ this.realm = realm;
+ }
+ }
+
+ class UniqueRealmTarget implements UniqueRealmObject {
+ protected String uniqueRealm;
+
+ @Override
+ public void setUniqueRealm(String uniqueRealm) {
+ this.uniqueRealm = uniqueRealm;
+ }
+
+ public String getUniqueRealm() {
+ return uniqueRealm;
+ }
+ }
+
+ class BothRealmsTarget implements RealmObject, UniqueRealmObject {
+
+ protected String realm;
+ protected String uniqueRealm;
+
+ public String getRealm() {
+ return realm;
+ }
+
+ @Override
+ public void setRealm(String realm) {
+ this.realm = realm;
+ }
+
+ public String getUniqueRealm() {
+ return uniqueRealm;
+ }
+
+ @Override
+ public void setUniqueRealm(String uniqueRealm) {
+ this.uniqueRealm = uniqueRealm;
+ }
+ }
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/wildcards/WildCardTest.java b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/wildcards/WildCardTest.java
index a228304c7..9e2712963 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/wildcards/WildCardTest.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/selection/methodgenerics/wildcards/WildCardTest.java
@@ -5,6 +5,7 @@
*/
package org.mapstruct.ap.test.selection.methodgenerics.wildcards;
+import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.ProcessorTest;
import org.mapstruct.ap.testutil.WithClasses;
import org.mapstruct.ap.testutil.runner.Compiler;
@@ -54,4 +55,23 @@ public class WildCardTest {
assertThat( target ).isNotNull();
assertThat( target.getProp() ).isEqualTo( typeC );
}
+
+ // Eclipse does not handle intersection types correctly (TODO: worthwhile to investigate?)
+ @ProcessorTest(Compiler.JDK)
+ @WithClasses(LifecycleIntersectionMapper.class)
+ @IssueKey("3036")
+ public void testLifecycleIntersection() {
+
+ LifecycleIntersectionMapper.RealmTarget realmTarget = LifecycleIntersectionMapper.INSTANCE.mapRealm( "test" );
+ assertThat( realmTarget.getRealm() ).isNull();
+
+ LifecycleIntersectionMapper.UniqueRealmTarget uniqueRealmTarget =
+ LifecycleIntersectionMapper.INSTANCE.mapUniqueRealm( "test" );
+ assertThat( uniqueRealmTarget.getUniqueRealm() ).isNull();
+
+ LifecycleIntersectionMapper.BothRealmsTarget bothRealmsTarget =
+ LifecycleIntersectionMapper.INSTANCE.mapBothRealms( "test" );
+ assertThat( bothRealmsTarget.getRealm() ).isEqualTo( "realm_test" );
+ assertThat( bothRealmsTarget.getUniqueRealm() ).isEqualTo( "uniqueRealm_test" );
+ }
}