diff --git a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java
index 75513dd6c..7e9175dd9 100644
--- a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java
+++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java
@@ -112,6 +112,11 @@ public class MavenIntegrationTest {
void protobufBuilderTest() {
}
+ @ProcessorTest(baseDir = "sealedSubclassTest")
+ @EnabledForJreRange(min = JRE.JAVA_17)
+ void sealedSubclassTest() {
+ }
+
@ProcessorTest(baseDir = "recordsTest", processorTypes = {
ProcessorTest.ProcessorType.JAVAC
})
diff --git a/integrationtest/src/test/resources/sealedSubclassTest/pom.xml b/integrationtest/src/test/resources/sealedSubclassTest/pom.xml
new file mode 100644
index 000000000..0706425e0
--- /dev/null
+++ b/integrationtest/src/test/resources/sealedSubclassTest/pom.xml
@@ -0,0 +1,102 @@
+
+
+
+ 4.0.0
+
+
+ org.mapstruct
+ mapstruct-it-parent
+ 1.0.0
+ ../pom.xml
+
+
+ sealedSubclassTest
+ jar
+
+
+
+ generate-via-compiler-plugin
+
+ false
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ \${compiler-id}
+ --enable-preview
+
+
+
+ org.eclipse.tycho
+ tycho-compiler-jdt
+ ${org.eclipse.tycho.compiler-jdt.version}
+
+
+
+
+
+
+
+ ${project.groupId}
+ mapstruct-processor
+ ${mapstruct.version}
+ provided
+
+
+
+
+ debug-forked-javac
+
+ false
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ default-compile
+
+ true
+
+ --enable-preview
+ -J-Xdebug
+ -J-Xnoagent
+ -J-Djava.compiler=NONE
+ -J-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+
+
+
+
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+ 1
+ --enable-preview
+
+
+
+
+
+
diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Bike.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Bike.java
new file mode 100644
index 000000000..5b68f52e6
--- /dev/null
+++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Bike.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.itest.sealedsubclass;
+
+public final class Bike extends Vehicle {
+ private int numberOfGears;
+
+ public int getNumberOfGears() {
+ return numberOfGears;
+ }
+
+ public void setNumberOfGears(int numberOfGears) {
+ this.numberOfGears = numberOfGears;
+ }
+}
diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/BikeDto.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/BikeDto.java
new file mode 100644
index 000000000..d51e95633
--- /dev/null
+++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/BikeDto.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.itest.sealedsubclass;
+
+public final class BikeDto extends VehicleDto {
+ private int numberOfGears;
+
+ public int getNumberOfGears() {
+ return numberOfGears;
+ }
+
+ public void setNumberOfGears(int numberOfGears) {
+ this.numberOfGears = numberOfGears;
+ }
+}
diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Car.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Car.java
new file mode 100644
index 000000000..0ed238e2a
--- /dev/null
+++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Car.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.itest.sealedsubclass;
+
+public final class Car extends Vehicle {
+ private boolean manual;
+
+ public boolean isManual() {
+ return manual;
+ }
+
+ public void setManual(boolean manual) {
+ this.manual = manual;
+ }
+
+}
diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/CarDto.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/CarDto.java
new file mode 100644
index 000000000..800bd23d3
--- /dev/null
+++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/CarDto.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.itest.sealedsubclass;
+
+public final class CarDto extends VehicleDto {
+ private boolean manual;
+
+ public boolean isManual() {
+ return manual;
+ }
+
+ public void setManual(boolean manual) {
+ this.manual = manual;
+ }
+}
diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Davidson.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Davidson.java
new file mode 100644
index 000000000..e883c14be
--- /dev/null
+++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Davidson.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.itest.sealedsubclass;
+
+public final class Davidson extends Motor {
+ private int numberOfExhausts;
+
+ public int getNumberOfExhausts() {
+ return numberOfExhausts;
+ }
+
+ public void setNumberOfExhausts(int numberOfExhausts) {
+ this.numberOfExhausts = numberOfExhausts;
+ }
+}
diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/DavidsonDto.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/DavidsonDto.java
new file mode 100644
index 000000000..e975226e3
--- /dev/null
+++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/DavidsonDto.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.itest.sealedsubclass;
+
+public final class DavidsonDto extends MotorDto {
+ private int numberOfExhausts;
+
+ public int getNumberOfExhausts() {
+ return numberOfExhausts;
+ }
+
+ public void setNumberOfExhausts(int numberOfExhausts) {
+ this.numberOfExhausts = numberOfExhausts;
+ }
+}
diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Harley.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Harley.java
new file mode 100644
index 000000000..87a48034c
--- /dev/null
+++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Harley.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.itest.sealedsubclass;
+
+public final class Harley extends Motor {
+ private int engineDb;
+
+ public int getEngineDb() {
+ return engineDb;
+ }
+
+ public void setEngineDb(int engineDb) {
+ this.engineDb = engineDb;
+ }
+}
diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/HarleyDto.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/HarleyDto.java
new file mode 100644
index 000000000..2090ee745
--- /dev/null
+++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/HarleyDto.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.itest.sealedsubclass;
+
+public final class HarleyDto extends MotorDto {
+ private int engineDb;
+
+ public int getEngineDb() {
+ return engineDb;
+ }
+
+ public void setEngineDb(int engineDb) {
+ this.engineDb = engineDb;
+ }
+}
diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Motor.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Motor.java
new file mode 100644
index 000000000..fcd5f4e4d
--- /dev/null
+++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Motor.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.itest.sealedsubclass;
+
+public sealed abstract class Motor extends Vehicle permits Harley, Davidson {
+ private int cc;
+
+ public int getCc() {
+ return cc;
+ }
+
+ public void setCc(int cc) {
+ this.cc = cc;
+ }
+}
diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/MotorDto.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/MotorDto.java
new file mode 100644
index 000000000..bd74eb929
--- /dev/null
+++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/MotorDto.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.itest.sealedsubclass;
+
+public sealed abstract class MotorDto extends VehicleDto permits HarleyDto, DavidsonDto {
+ private int cc;
+
+ public int getCc() {
+ return cc;
+ }
+
+ public void setCc(int cc) {
+ this.cc = cc;
+ }
+}
diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/SealedSubclassMapper.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/SealedSubclassMapper.java
new file mode 100644
index 000000000..b37f62368
--- /dev/null
+++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/SealedSubclassMapper.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.itest.sealedsubclass;
+
+import org.mapstruct.InheritInverseConfiguration;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.SubclassMapping;
+import org.mapstruct.factory.Mappers;
+
+@Mapper
+public interface SealedSubclassMapper {
+ SealedSubclassMapper INSTANCE = Mappers.getMapper( SealedSubclassMapper.class );
+
+ VehicleCollectionDto map(VehicleCollection vehicles);
+
+ @SubclassMapping( source = Car.class, target = CarDto.class )
+ @SubclassMapping( source = Bike.class, target = BikeDto.class )
+ @SubclassMapping( source = Harley.class, target = HarleyDto.class )
+ @SubclassMapping( source = Davidson.class, target = DavidsonDto.class )
+ @Mapping( source = "vehicleManufacturingCompany", target = "maker")
+ VehicleDto map(Vehicle vehicle);
+
+ VehicleCollection mapInverse(VehicleCollectionDto vehicles);
+
+ @InheritInverseConfiguration
+ Vehicle mapInverse(VehicleDto dto);
+}
diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Vehicle.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Vehicle.java
new file mode 100644
index 000000000..2a4e7560f
--- /dev/null
+++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/Vehicle.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.itest.sealedsubclass;
+
+public abstract sealed class Vehicle permits Bike, Car, Motor {
+ private String name;
+ private String vehicleManufacturingCompany;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getVehicleManufacturingCompany() {
+ return vehicleManufacturingCompany;
+ }
+
+ public void setVehicleManufacturingCompany(String vehicleManufacturingCompany) {
+ this.vehicleManufacturingCompany = vehicleManufacturingCompany;
+ }
+}
diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/VehicleCollection.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/VehicleCollection.java
new file mode 100644
index 000000000..1ada92a29
--- /dev/null
+++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/VehicleCollection.java
@@ -0,0 +1,17 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.itest.sealedsubclass;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+public class VehicleCollection {
+ private Collection vehicles = new ArrayList<>();
+
+ public Collection getVehicles() {
+ return vehicles;
+ }
+}
diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/VehicleCollectionDto.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/VehicleCollectionDto.java
new file mode 100644
index 000000000..0cae41217
--- /dev/null
+++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/VehicleCollectionDto.java
@@ -0,0 +1,17 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.itest.sealedsubclass;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+public class VehicleCollectionDto {
+ private Collection vehicles = new ArrayList<>();
+
+ public Collection getVehicles() {
+ return vehicles;
+ }
+}
diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/VehicleDto.java b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/VehicleDto.java
new file mode 100644
index 000000000..8c50bdcad
--- /dev/null
+++ b/integrationtest/src/test/resources/sealedSubclassTest/src/main/java/org/mapstruct/itest/sealedsubclass/VehicleDto.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.itest.sealedsubclass;
+
+public abstract sealed class VehicleDto permits CarDto, BikeDto, MotorDto {
+ private String name;
+ private String maker;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getMaker() {
+ return maker;
+ }
+
+ public void setMaker(String maker) {
+ this.maker = maker;
+ }
+}
diff --git a/integrationtest/src/test/resources/sealedSubclassTest/src/test/java/org/mapstruct/itest/sealedsubclass/SealedSubclassTest.java b/integrationtest/src/test/resources/sealedSubclassTest/src/test/java/org/mapstruct/itest/sealedsubclass/SealedSubclassTest.java
new file mode 100644
index 000000000..379341ff6
--- /dev/null
+++ b/integrationtest/src/test/resources/sealedSubclassTest/src/test/java/org/mapstruct/itest/sealedsubclass/SealedSubclassTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.itest.sealedsubclass;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.Test;
+
+public class SealedSubclassTest {
+
+ @Test
+ public void mappingIsDoneUsingSubclassMapping() {
+ VehicleCollection vehicles = new VehicleCollection();
+ vehicles.getVehicles().add( new Car() );
+ vehicles.getVehicles().add( new Bike() );
+ vehicles.getVehicles().add( new Harley() );
+ vehicles.getVehicles().add( new Davidson() );
+
+ VehicleCollectionDto result = SealedSubclassMapper.INSTANCE.map( vehicles );
+
+ assertThat( result.getVehicles() ).doesNotContainNull();
+ assertThat( result.getVehicles() ) // remove generic so that test works.
+ .extracting( vehicle -> (Class) vehicle.getClass() )
+ .containsExactly( CarDto.class, BikeDto.class, HarleyDto.class, DavidsonDto.class );
+ }
+
+ @Test
+ public void inverseMappingIsDoneUsingSubclassMapping() {
+ VehicleCollectionDto vehicles = new VehicleCollectionDto();
+ vehicles.getVehicles().add( new CarDto() );
+ vehicles.getVehicles().add( new BikeDto() );
+ vehicles.getVehicles().add( new HarleyDto() );
+ vehicles.getVehicles().add( new DavidsonDto() );
+
+ VehicleCollection result = SealedSubclassMapper.INSTANCE.mapInverse( vehicles );
+
+ assertThat( result.getVehicles() ).doesNotContainNull();
+ assertThat( result.getVehicles() ) // remove generic so that test works.
+ .extracting( vehicle -> (Class) vehicle.getClass() )
+ .containsExactly( Car.class, Bike.class, Harley.class, Davidson.class );
+ }
+
+ @Test
+ public void subclassMappingInheritsInverseMapping() {
+ VehicleCollectionDto vehiclesDto = new VehicleCollectionDto();
+ CarDto carDto = new CarDto();
+ carDto.setMaker( "BenZ" );
+ vehiclesDto.getVehicles().add( carDto );
+
+ VehicleCollection result = SealedSubclassMapper.INSTANCE.mapInverse( vehiclesDto );
+
+ assertThat( result.getVehicles() )
+ .extracting( Vehicle::getVehicleManufacturingCompany )
+ .containsExactly( "BenZ" );
+ }
+}
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 84cfd1828..cf3e23db9 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
@@ -446,8 +446,39 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
}
private boolean isAbstractReturnTypeAllowed() {
- return method.getOptions().getBeanMapping().getSubclassExhaustiveStrategy().isAbstractReturnTypeAllowed()
- && !method.getOptions().getSubclassMappings().isEmpty();
+ return !method.getOptions().getSubclassMappings().isEmpty()
+ && ( method.getOptions().getBeanMapping().getSubclassExhaustiveStrategy().isAbstractReturnTypeAllowed()
+ || isCorrectlySealed() );
+ }
+
+ private boolean isCorrectlySealed() {
+ Type mappingSourceType = method.getMappingSourceType();
+ return isCorrectlySealed( mappingSourceType );
+ }
+
+ private boolean isCorrectlySealed(Type mappingSourceType) {
+ if ( mappingSourceType.isSealed() ) {
+ List extends TypeMirror> unusedPermittedSubclasses =
+ new ArrayList<>( mappingSourceType.getPermittedSubclasses() );
+ method.getOptions().getSubclassMappings().forEach( subClassOption -> {
+ for (Iterator extends TypeMirror> iterator = unusedPermittedSubclasses.iterator();
+ iterator.hasNext(); ) {
+ if ( ctx.getTypeUtils().isSameType( iterator.next(), subClassOption.getSource() ) ) {
+ iterator.remove();
+ }
+ }
+ } );
+ for ( Iterator extends TypeMirror> iterator = unusedPermittedSubclasses.iterator();
+ iterator.hasNext(); ) {
+ TypeMirror typeMirror = iterator.next();
+ Type type = ctx.getTypeFactory().getType( typeMirror );
+ if ( type.isAbstract() && isCorrectlySealed( type ) ) {
+ iterator.remove();
+ }
+ }
+ return unusedPermittedSubclasses.isEmpty();
+ }
+ return false;
}
private void initializeMappingReferencesIfNeeded(Type resultTypeToMap) {
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 689490626..4254913d8 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
@@ -5,6 +5,8 @@
*/
package org.mapstruct.ap.internal.model.common;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
@@ -53,6 +55,7 @@ import org.mapstruct.ap.internal.util.accessor.MapValueAccessor;
import org.mapstruct.ap.internal.util.accessor.PresenceCheckAccessor;
import org.mapstruct.ap.internal.util.accessor.ReadAccessor;
+import static java.util.Collections.emptyList;
import static org.mapstruct.ap.internal.util.Collections.first;
/**
@@ -67,6 +70,18 @@ import static org.mapstruct.ap.internal.util.Collections.first;
* @author Filip Hrisafov
*/
public class Type extends ModelElement implements Comparable {
+ private static final Method SEALED_PERMITTED_SUBCLASSES_METHOD;
+
+ static {
+ Method permittedSubclassesMethod;
+ try {
+ permittedSubclassesMethod = TypeElement.class.getMethod( "getPermittedSubclasses" );
+ }
+ catch ( NoSuchMethodException e ) {
+ permittedSubclassesMethod = null;
+ }
+ SEALED_PERMITTED_SUBCLASSES_METHOD = permittedSubclassesMethod;
+ }
private final TypeUtils typeUtils;
private final ElementUtils elementUtils;
@@ -1661,4 +1676,27 @@ public class Type extends ModelElement implements Comparable {
return "java.util.EnumSet".equals( getFullyQualifiedName() );
}
+ /**
+ * return true if this type is a java 17+ sealed class
+ */
+ public boolean isSealed() {
+ return typeElement.getModifiers().stream().map( Modifier::name ).anyMatch( "SEALED"::equals );
+ }
+
+ /**
+ * return the list of permitted TypeMirrors for the java 17+ sealed class
+ */
+ @SuppressWarnings( "unchecked" )
+ public List extends TypeMirror> getPermittedSubclasses() {
+ if (SEALED_PERMITTED_SUBCLASSES_METHOD == null) {
+ return emptyList();
+ }
+ try {
+ return (List extends TypeMirror>) SEALED_PERMITTED_SUBCLASSES_METHOD.invoke( typeElement );
+ }
+ catch ( IllegalAccessException | IllegalArgumentException | InvocationTargetException e ) {
+ return emptyList();
+ }
+ }
+
}