Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -222,4 +222,12 @@ void usesTypeGenerationTestEclipse() {
void faultyAstModifyingProcessor() {
}

/**
* For sealed interface, create an impl class with the switch case.
*/
@ProcessorTest(baseDir = "sealedInterfaceTest")
@EnabledForJreRange(min = JRE.JAVA_21)
void sealedInterfaceSubclassMappingTest() {
}

}
103 changes: 103 additions & 0 deletions integrationtest/src/test/resources/sealedInterfaceTest/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--

Copyright MapStruct Authors.

Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0

-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-it-parent</artifactId>
<version>1.0.0</version>
<relativePath>../pom.xml</relativePath>
</parent>

<artifactId>sealedInterfaceTest</artifactId>
<packaging>jar</packaging>

<profiles>
<profile>
<id>generate-via-compiler-plugin</id>
<activation>
<activeByDefault>false</activeByDefault>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<compilerArgument
combine.self="override"></compilerArgument>
<compilerId>\${compiler-id}</compilerId>
<compilerArgs>--enable-preview</compilerArgs>
</configuration>
<dependencies>
<dependency>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-compiler-jdt</artifactId>
<version>${org.eclipse.tycho.compiler-jdt.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</profile>
<profile>
<id>debug-forked-javac</id>
<activation>
<activeByDefault>false</activeByDefault>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<id>default-compile</id>
<configuration>
<fork>true</fork>
<compilerArgs>
<arg>--enable-preview</arg>
<arg>-J-Xdebug</arg>
<arg>-J-Xnoagent</arg>
<arg>-J-Djava.compiler=NONE</arg>
<arg>-J-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000</arg>
</compilerArgs>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<forkCount>1</forkCount>
<argLine>--enable-preview</argLine>
</configuration>
</plugin>
</plugins>
</build>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* 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.sealedinterface;

public sealed interface From permits FromA, FromB {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* 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.sealedinterface;

public record FromA(int a) implements From {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* 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.sealedinterface;

public record FromB(int b) implements From {}
Original file line number Diff line number Diff line change
@@ -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.itest.sealedinterface;

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.ReportingPolicy;
import org.mapstruct.SubclassMapping;
import org.mapstruct.factory.Mappers;

@Mapper(unmappedTargetPolicy = ReportingPolicy.WARN)
public interface Issue3836Mapper {

Issue3836Mapper INSTANCE = Mappers.getMapper( Issue3836Mapper.class );

@Mapping(source = "a", target = "to")
To map(FromA from);

@Mapping(source = "b", target = "to")
To map(FromB fromB);

@SubclassMapping(source = FromA.class, target = To.class)
@SubclassMapping(source = FromB.class, target = To.class)
To map(From from);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* 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.sealedinterface;

public record To(int to) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* 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.sealedinterface;

import org.junit.Test;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

import static org.assertj.core.api.Assertions.assertThat;

public class SealedInterfaceTest {

private final Issue3836Mapper mapper = Issue3836Mapper.INSTANCE;

@Test
public void shouldGeneratePatternSwitchInPolymorphicMapMethod() throws IOException {
Path generatedFile = Path.of("target/generated-sources/annotations/org/mapstruct/itest/sealedinterface/Issue3836MapperImpl.java");
String content = Files.readString(generatedFile);
assertThat(content.contains("switch (from) {")).isTrue();
assertThat(content.contains("case FromA fromA ->")).isTrue();
assertThat(content.contains("return map( (FromA) from );")).isTrue();
assertThat(content.contains("case FromB fromB ->")).isTrue();
assertThat(content.contains("return map( (FromB) from );")).isTrue();
assertThat(content.contains("if (from instanceof")).isFalse();
}

@Test
public void shouldMapFromAToTo() {
FromA fromA = new FromA(100);

To result = mapper.map(fromA);

assertThat(result).isNotNull();
assertThat(result.to()).isEqualTo(100);
}

@Test
public void shouldMapFromBToTo() {
FromB fromB = new FromB(200);

To result = mapper.map(fromB);

assertThat(result).isNotNull();
assertThat(result.to()).isEqualTo(200);
}

@Test
public void shouldMapFromInterfaceWithFromAInstance() {
From from = new FromA(300);

To result = mapper.map(from);

assertThat(result).isNotNull();
assertThat(result.to()).isEqualTo(300);
}

@Test
public void shouldMapFromInterfaceWithFromBInstance() {
From from = new FromB(400);

To result = mapper.map(from);

assertThat(result).isNotNull();
assertThat(result.to()).isEqualTo(400);
}

@Test
public void shouldReturnNullWhenInputIsNull() {
assertThat(mapper.map((FromA) null)).isNull();
assertThat(mapper.map((FromB) null)).isNull();
assertThat(mapper.map((From) null)).isNull();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -532,18 +532,20 @@ private SubclassMapping createSubclassMapping(SubclassMappingOptions subclassMap
targetType,
mappingReferences ) );
String sourceArgument = null;
Type sourceArgumentType = null;
for ( Parameter parameter : method.getSourceParameters() ) {
if ( ctx
.getTypeUtils()
.isAssignable( sourceType.getTypeMirror(), parameter.getType().getTypeMirror() ) ) {
sourceArgument = parameter.getName();
sourceArgumentType = parameter.getType();
if ( assignment != null ) {
assignment.setSourceLocalVarName(
"(" + sourceType.createReferenceName() + ") " + sourceArgument );
}
}
}
return new SubclassMapping( sourceType, sourceArgument, targetType, assignment );
return new SubclassMapping( sourceType, sourceArgument, sourceArgumentType, targetType, assignment );
}

private boolean isAbstractReturnTypeAllowed() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,13 @@ public class SubclassMapping extends ModelElement {
private final Type targetType;
private final Assignment assignment;
private final String sourceArgument;
private final Type sourceArgumentType;

public SubclassMapping(Type sourceType, String sourceArgument, Type targetType, Assignment assignment) {
public SubclassMapping(Type sourceType, String sourceArgument, Type sourceArgumentType,
Type targetType, Assignment assignment) {
this.sourceType = sourceType;
this.sourceArgument = sourceArgument;
this.sourceArgumentType = sourceArgumentType;
this.targetType = targetType;
this.assignment = assignment;
}
Expand All @@ -40,6 +43,10 @@ public Type getSourceType() {
return sourceType;
}

public Type getSourceArgumentType() {
return sourceArgumentType;
}

@Override
public Set<Type> getImportTypes() {
return Collections.singleton( sourceType );
Expand Down
Loading
Loading