diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 267702826..d17718370 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -704,6 +704,14 @@ public class BeanMappingMethod extends NormalTypeMappingMethod { } private ConstructorAccessor getConstructorAccessor(Type type) { + if ( type.isAbstract() ) { + // We cannot construct abstract classes. + // Usually we won't reach here, + // but if SubclassMapping is used with SubclassExhaustiveStrategy#RUNTIME_EXCEPTION + // then we will still generate the code. + // We shouldn't generate anything for those abstract types + return null; + } if ( type.isRecord() ) { // If the type is a record then just get the record components and use then @@ -1783,7 +1791,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod { } public boolean isAbstractReturnType() { - return getFactoryMethod() == null && !hasConstructorMappings() && returnTypeToConstruct != null + return getFactoryMethod() == null && returnTypeToConstruct != null && returnTypeToConstruct.isAbstract(); } diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2891/Issue2891Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2891/Issue2891Mapper.java new file mode 100644 index 000000000..205978aeb --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2891/Issue2891Mapper.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.bugs._2891; + +import org.mapstruct.BeanMapping; +import org.mapstruct.Mapper; +import org.mapstruct.SubclassExhaustiveStrategy; +import org.mapstruct.SubclassMapping; + +/** + * @author Sergei Portnov + */ +@Mapper +public interface Issue2891Mapper { + + @BeanMapping(subclassExhaustiveStrategy = SubclassExhaustiveStrategy.RUNTIME_EXCEPTION) + @SubclassMapping(source = Source1.class, target = Target1.class) + @SubclassMapping(source = Source2.class, target = Target2.class) + AbstractTarget map(AbstractSource source); + + abstract class AbstractTarget { + + private final String name; + + protected AbstractTarget(String name) { + this.name = name; + } + + public String getName() { + return name; + } + } + + class Target1 extends AbstractTarget { + + protected Target1(String name) { + super( name ); + } + } + + class Target2 extends AbstractTarget { + + protected Target2(String name) { + super( name ); + } + } + + abstract class AbstractSource { + + private final String name; + + protected AbstractSource(String name) { + this.name = name; + } + + public String getName() { + return name; + } + } + + class Source1 extends AbstractSource { + protected Source1(String name) { + super( name ); + } + } + + class Source2 extends AbstractSource { + + protected Source2(String name) { + super( name ); + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2891/Issue2891Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2891/Issue2891Test.java new file mode 100644 index 000000000..c82d5aa63 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2891/Issue2891Test.java @@ -0,0 +1,30 @@ +/* + * 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._2891; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.GeneratedSource; + +/** + * @author Sergei Portnov + */ +@WithClasses({ + Issue2891Mapper.class +}) +@IssueKey("2891") +class Issue2891Test { + + @RegisterExtension + final GeneratedSource generatedSource = new GeneratedSource() + .addComparisonToFixtureFor( Issue2891Mapper.class ); + + @ProcessorTest + void shouldCompile() { + } +} diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_2891/Issue2891MapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_2891/Issue2891MapperImpl.java new file mode 100644 index 000000000..64ea2dc83 --- /dev/null +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/bugs/_2891/Issue2891MapperImpl.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.bugs._2891; + +import javax.annotation.processing.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2022-06-18T14:48:32+0300", + comments = "version: , compiler: Eclipse JDT (Batch) 3.20.0.v20191203-2131, environment: Java 11.0.15.1 (BellSoft)" +) +public class Issue2891MapperImpl implements Issue2891Mapper { + + @Override + public AbstractTarget map(AbstractSource source) { + if ( source == null ) { + return null; + } + + if (source instanceof Source1) { + return source1ToTarget1( (Source1) source ); + } + else if (source instanceof Source2) { + return source2ToTarget2( (Source2) source ); + } + else { + throw new IllegalArgumentException("Not all subclasses are supported for this mapping. Missing for " + source.getClass()); + } + } + + protected Target1 source1ToTarget1(Source1 source1) { + if ( source1 == null ) { + return null; + } + + String name = null; + + name = source1.getName(); + + Target1 target1 = new Target1( name ); + + return target1; + } + + protected Target2 source2ToTarget2(Source2 source2) { + if ( source2 == null ) { + return null; + } + + String name = null; + + name = source2.getName(); + + Target2 target2 = new Target2( name ); + + return target2; + } +}