diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index b02363162..6d80d2da8 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -12,7 +12,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- java: [11, 13, 16, 17]
+ java: [13, 16, 17]
name: 'Linux JDK ${{ matrix.java }}'
runs-on: ubuntu-latest
steps:
@@ -25,15 +25,15 @@ jobs:
- name: 'Test'
run: ./mvnw ${MAVEN_ARGS} -Djacoco.skip=true install -DskipDistribution=true
linux:
- name: 'Linux JDK 8'
+ name: 'Linux JDK 11'
runs-on: ubuntu-latest
steps:
- name: 'Checkout'
uses: actions/checkout@v2
- - name: 'Set up JDK 8'
+ - name: 'Set up JDK 11'
uses: actions/setup-java@v1
with:
- java-version: 8
+ java-version: 11
- name: 'Test'
run: ./mvnw ${MAVEN_ARGS} install
- name: 'Generate coverage report'
@@ -43,25 +43,43 @@ jobs:
- name: 'Publish Snapshots'
if: github.event_name == 'push' && github.ref == 'refs/heads/master' && github.repository == 'mapstruct/mapstruct'
run: ./mvnw -s etc/ci-settings.xml -DskipTests=true -DskipDistribution=true deploy
+ linux-jdk-8:
+ name: 'Linux JDK 8'
+ runs-on: ubuntu-latest
+ steps:
+ - name: 'Checkout'
+ uses: actions/checkout@v2
+ - name: 'Set up JDK 11 for building everything'
+ uses: actions/setup-java@v1
+ with:
+ java-version: 11
+ - name: 'Install Processor'
+ run: ./mvnw ${MAVEN_ARGS} -DskipTests install -pl processor -am
+ - name: 'Set up JDK 8 for running integration tests'
+ uses: actions/setup-java@v1
+ with:
+ java-version: 8
+ - name: 'Run integration tests'
+ run: ./mvnw ${MAVEN_ARGS} verify -pl integrationtest
windows:
name: 'Windows'
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- - name: 'Set up JDK 8'
+ - name: 'Set up JDK 11'
uses: actions/setup-java@v1
with:
- java-version: 8
+ java-version: 11
- name: 'Test'
- run: ./mvnw ${MAVEN_ARGS} install
+ run: ./mvnw %MAVEN_ARGS% install
mac:
name: 'Mac OS'
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- - name: 'Set up JDK 8'
+ - name: 'Set up JDK 11'
uses: actions/setup-java@v1
with:
- java-version: 8
+ java-version: 11
- name: 'Test'
run: ./mvnw ${MAVEN_ARGS} install
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 9696542f5..4f4182d5c 100644
--- a/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java
+++ b/integrationtest/src/test/java/org/mapstruct/itest/tests/MavenIntegrationTest.java
@@ -85,6 +85,14 @@ public class MavenIntegrationTest {
void lombokBuilderTest() {
}
+ @ProcessorTest(baseDir = "lombokModuleTest", processorTypes = {
+ ProcessorTest.ProcessorType.JAVAC,
+ ProcessorTest.ProcessorType.JAVAC_WITH_PATHS
+ })
+ @EnabledForJreRange(min = JRE.JAVA_11)
+ void lombokModuleTest() {
+ }
+
@ProcessorTest(baseDir = "namingStrategyTest", processorTypes = {
ProcessorTest.ProcessorType.JAVAC
})
diff --git a/integrationtest/src/test/resources/lombokModuleTest/pom.xml b/integrationtest/src/test/resources/lombokModuleTest/pom.xml
new file mode 100644
index 000000000..16b32cfb4
--- /dev/null
+++ b/integrationtest/src/test/resources/lombokModuleTest/pom.xml
@@ -0,0 +1,85 @@
+
+
+
+ 4.0.0
+
+
+ org.mapstruct
+ mapstruct-it-parent
+ 1.0.0
+ ../pom.xml
+
+
+ lombokModuleIntegrationTest
+ jar
+
+
+ 11
+ 11
+
+
+
+
+ org.projectlombok
+ lombok
+ compile
+
+
+ org.projectlombok
+ lombok-mapstruct-binding
+ 0.2.0
+ compile
+
+
+
+
+
+ generate-via-compiler-plugin-with-annotation-processor-paths
+
+ false
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ \${compiler-id}
+
+
+ ${project.groupId}
+ mapstruct-processor
+ ${mapstruct.version}
+
+
+ org.projectlombok
+ lombok
+ 1.18.22
+
+
+ org.projectlombok
+ lombok-mapstruct-binding
+ 0.2.0
+
+
+
+
+
+ org.eclipse.tycho
+ tycho-compiler-jdt
+ ${org.eclipse.tycho.compiler-jdt.version}
+
+
+
+
+
+
+
+
diff --git a/integrationtest/src/test/resources/lombokModuleTest/src/main/java/module-info.java b/integrationtest/src/test/resources/lombokModuleTest/src/main/java/module-info.java
new file mode 100644
index 000000000..9bc85bea8
--- /dev/null
+++ b/integrationtest/src/test/resources/lombokModuleTest/src/main/java/module-info.java
@@ -0,0 +1,9 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
+ */
+module org.example {
+ requires org.mapstruct;
+ requires lombok;
+}
diff --git a/integrationtest/src/test/resources/lombokModuleTest/src/main/java/org/mapstruct/itest/lombok/Address.java b/integrationtest/src/test/resources/lombokModuleTest/src/main/java/org/mapstruct/itest/lombok/Address.java
new file mode 100644
index 000000000..f83c6881c
--- /dev/null
+++ b/integrationtest/src/test/resources/lombokModuleTest/src/main/java/org/mapstruct/itest/lombok/Address.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.lombok;
+
+public class Address {
+ private final String addressLine;
+
+ public Address(String addressLine) {
+ this.addressLine = addressLine;
+ }
+
+ public String getAddressLine() {
+ return addressLine;
+ }
+}
diff --git a/integrationtest/src/test/resources/lombokModuleTest/src/main/java/org/mapstruct/itest/lombok/AddressDto.java b/integrationtest/src/test/resources/lombokModuleTest/src/main/java/org/mapstruct/itest/lombok/AddressDto.java
new file mode 100644
index 000000000..d6b4f30f8
--- /dev/null
+++ b/integrationtest/src/test/resources/lombokModuleTest/src/main/java/org/mapstruct/itest/lombok/AddressDto.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.lombok;
+
+public class AddressDto {
+
+ private final String addressLine;
+
+ public AddressDto(String addressLine) {
+ this.addressLine = addressLine;
+ }
+
+ public String getAddressLine() {
+ return addressLine;
+ }
+}
diff --git a/integrationtest/src/test/resources/lombokModuleTest/src/main/java/org/mapstruct/itest/lombok/Person.java b/integrationtest/src/test/resources/lombokModuleTest/src/main/java/org/mapstruct/itest/lombok/Person.java
new file mode 100644
index 000000000..b6ca47fca
--- /dev/null
+++ b/integrationtest/src/test/resources/lombokModuleTest/src/main/java/org/mapstruct/itest/lombok/Person.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.itest.lombok;
+
+public class Person {
+ private final String name;
+ private final int age;
+ private final Address address;
+
+ public Person(String name, int age, Address address) {
+ this.name = name;
+ this.age = age;
+ this.address = address;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public Address getAddress() {
+ return address;
+ }
+}
diff --git a/integrationtest/src/test/resources/lombokModuleTest/src/main/java/org/mapstruct/itest/lombok/PersonDto.java b/integrationtest/src/test/resources/lombokModuleTest/src/main/java/org/mapstruct/itest/lombok/PersonDto.java
new file mode 100644
index 000000000..322377866
--- /dev/null
+++ b/integrationtest/src/test/resources/lombokModuleTest/src/main/java/org/mapstruct/itest/lombok/PersonDto.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.itest.lombok;
+
+public class PersonDto {
+ private final String name;
+ private final int age;
+ private final AddressDto address;
+
+ public PersonDto(String name, int age, AddressDto address) {
+ this.name = name;
+ this.age = age;
+ this.address = address;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public AddressDto getAddress() {
+ return address;
+ }
+}
diff --git a/integrationtest/src/test/resources/lombokModuleTest/src/main/java/org/mapstruct/itest/lombok/PersonMapper.java b/integrationtest/src/test/resources/lombokModuleTest/src/main/java/org/mapstruct/itest/lombok/PersonMapper.java
new file mode 100644
index 000000000..27184bad2
--- /dev/null
+++ b/integrationtest/src/test/resources/lombokModuleTest/src/main/java/org/mapstruct/itest/lombok/PersonMapper.java
@@ -0,0 +1,21 @@
+/*
+ * 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.lombok;
+
+import org.mapstruct.Mapper;
+import org.mapstruct.ReportingPolicy;
+import org.mapstruct.factory.Mappers;
+
+/**
+ */
+@Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR)
+public interface PersonMapper {
+
+ PersonMapper INSTANCE = Mappers.getMapper( PersonMapper.class );
+
+ Person fromDto(PersonDto personDto);
+ PersonDto toDto(Person personDto);
+}
diff --git a/integrationtest/src/test/resources/lombokModuleTest/src/test/java/org/mapstruct/itest/lombok/LombokMapperTest.java b/integrationtest/src/test/resources/lombokModuleTest/src/test/java/org/mapstruct/itest/lombok/LombokMapperTest.java
new file mode 100644
index 000000000..f8b702896
--- /dev/null
+++ b/integrationtest/src/test/resources/lombokModuleTest/src/test/java/org/mapstruct/itest/lombok/LombokMapperTest.java
@@ -0,0 +1,37 @@
+/*
+ * 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.lombok;
+
+import org.junit.Test;
+import org.mapstruct.factory.Mappers;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Test for generation of Lombok Builder Mapper implementations
+ *
+ * @author Eric Martineau
+ */
+public class LombokMapperTest {
+
+ @Test
+ public void testSimpleImmutableBuilderHappyPath() {
+ PersonDto personDto = PersonMapper.INSTANCE.toDto( new Person( "Bob", 33, new Address( "Wild Drive" ) ) );
+ assertThat( personDto.getAge() ).isEqualTo( 33 );
+ assertThat( personDto.getName() ).isEqualTo( "Bob" );
+ assertThat( personDto.getAddress() ).isNotNull();
+ assertThat( personDto.getAddress().getAddressLine() ).isEqualTo( "Wild Drive" );
+ }
+
+ @Test
+ public void testLombokToImmutable() {
+ Person person = PersonMapper.INSTANCE.fromDto( new PersonDto( "Bob", 33, new AddressDto( "Wild Drive" ) ) );
+ assertThat( person.getAge() ).isEqualTo( 33 );
+ assertThat( person.getName() ).isEqualTo( "Bob" );
+ assertThat( person.getAddress() ).isNotNull();
+ assertThat( person.getAddress().getAddressLine() ).isEqualTo( "Wild Drive" );
+ }
+}
diff --git a/parent/pom.xml b/parent/pom.xml
index 1f75e9d6d..4ca3d9b2c 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -36,6 +36,11 @@
jdt_apt
+
+ 1.8
@@ -214,7 +219,7 @@
org.projectlombok
lombok
- 1.18.20
+ 1.18.22
org.immutables
@@ -666,6 +671,9 @@
java18
1.0
+
+ org.mapstruct.ap.internal.util.IgnoreJRERequirement
+
@@ -685,7 +693,7 @@
- [1.8,)
+ [${minimum.java.version},)
diff --git a/processor/pom.xml b/processor/pom.xml
index 2a3c38abf..ec3f20a24 100644
--- a/processor/pom.xml
+++ b/processor/pom.xml
@@ -24,6 +24,7 @@
+ 11
diff --git a/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java b/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java
index 0cc6b7cce..4f9e0fa38 100644
--- a/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java
+++ b/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java
@@ -269,7 +269,11 @@ public class MappingProcessor extends AbstractProcessor {
// of one outer interface
List extends Element> tst = mapperElement.getEnclosedElements();
ProcessorContext context = new DefaultModelElementProcessorContext(
- processingEnv, options, roundContext, getDeclaredTypesNotToBeImported( mapperElement )
+ processingEnv,
+ options,
+ roundContext,
+ getDeclaredTypesNotToBeImported( mapperElement ),
+ mapperElement
);
processMapperTypeElement( context, mapperElement );
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultModelElementProcessorContext.java b/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultModelElementProcessorContext.java
index d6a05cec3..51ea5dd78 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultModelElementProcessorContext.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/processor/DefaultModelElementProcessorContext.java
@@ -13,6 +13,7 @@ import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic.Kind;
import org.mapstruct.ap.internal.model.common.TypeFactory;
@@ -46,14 +47,14 @@ public class DefaultModelElementProcessorContext implements ProcessorContext {
private final RoundContext roundContext;
public DefaultModelElementProcessorContext(ProcessingEnvironment processingEnvironment, Options options,
- RoundContext roundContext, Map notToBeImported) {
+ RoundContext roundContext, Map notToBeImported, TypeElement mapperElement) {
this.processingEnvironment = processingEnvironment;
this.messager = new DelegatingMessager( processingEnvironment.getMessager(), options.isVerbose() );
this.accessorNaming = roundContext.getAnnotationProcessorContext().getAccessorNaming();
this.versionInformation = DefaultVersionInformation.fromProcessingEnvironment( processingEnvironment );
this.delegatingTypes = TypeUtils.create( processingEnvironment, versionInformation );
- this.delegatingElements = ElementUtils.create( processingEnvironment, versionInformation );
+ this.delegatingElements = ElementUtils.create( processingEnvironment, versionInformation, mapperElement );
this.roundContext = roundContext;
this.typeFactory = new TypeFactory(
delegatingElements,
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/AbstractElementUtilsDecorator.java b/processor/src/main/java/org/mapstruct/ap/internal/util/AbstractElementUtilsDecorator.java
index 14734b9c8..72a44d106 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/util/AbstractElementUtilsDecorator.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/util/AbstractElementUtilsDecorator.java
@@ -13,11 +13,13 @@ import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
+import javax.lang.model.element.ModuleElement;
import javax.lang.model.element.Name;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
@@ -35,19 +37,44 @@ import static javax.lang.model.util.ElementFilter.methodsIn;
public abstract class AbstractElementUtilsDecorator implements ElementUtils {
private final Elements delegate;
+ /**
+ * The module element when running with the module system,
+ * {@code null} otherwise.
+ */
+ private final Element moduleElement;
- AbstractElementUtilsDecorator(ProcessingEnvironment processingEnv) {
+ @IgnoreJRERequirement
+ AbstractElementUtilsDecorator(ProcessingEnvironment processingEnv, TypeElement mapperElement) {
this.delegate = processingEnv.getElementUtils();
+ if ( SourceVersion.RELEASE_8.compareTo( processingEnv.getSourceVersion() ) >= 0 ) {
+ // We are running with Java 8 or lower
+ this.moduleElement = null;
+ }
+ else {
+ this.moduleElement = this.delegate.getModuleOf( mapperElement );
+ }
}
@Override
+ @IgnoreJRERequirement
public PackageElement getPackageElement(CharSequence name) {
- return delegate.getPackageElement( name );
+ if ( this.moduleElement == null ) {
+ return this.delegate.getPackageElement( name );
+ }
+
+ // If the module element is not null then we must be running on Java 8+
+ return this.delegate.getPackageElement( (ModuleElement) moduleElement, name );
}
@Override
+ @IgnoreJRERequirement
public TypeElement getTypeElement(CharSequence name) {
- return delegate.getTypeElement( name );
+ if ( this.moduleElement == null ) {
+ return this.delegate.getTypeElement( name );
+ }
+
+ // If the module element is not null then we must be running on Java 8+
+ return this.delegate.getTypeElement( (ModuleElement) moduleElement, name );
}
@Override
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/EclipseElementUtilsDecorator.java b/processor/src/main/java/org/mapstruct/ap/internal/util/EclipseElementUtilsDecorator.java
index aaf1bb7e2..3c1dc84d3 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/util/EclipseElementUtilsDecorator.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/util/EclipseElementUtilsDecorator.java
@@ -13,8 +13,8 @@ public class EclipseElementUtilsDecorator extends AbstractElementUtilsDecorator
private final Elements delegate;
- EclipseElementUtilsDecorator(ProcessingEnvironment processingEnv) {
- super( processingEnv );
+ EclipseElementUtilsDecorator(ProcessingEnvironment processingEnv, TypeElement mapperElement) {
+ super( processingEnv, mapperElement );
this.delegate = processingEnv.getElementUtils();
}
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/ElementUtils.java b/processor/src/main/java/org/mapstruct/ap/internal/util/ElementUtils.java
index fdd221c1a..3e026cde8 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/util/ElementUtils.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/util/ElementUtils.java
@@ -16,12 +16,13 @@ import org.mapstruct.ap.internal.version.VersionInformation;
public interface ElementUtils extends Elements {
- static ElementUtils create(ProcessingEnvironment processingEnvironment, VersionInformation info ) {
+ static ElementUtils create(ProcessingEnvironment processingEnvironment, VersionInformation info,
+ TypeElement mapperElement) {
if ( info.isEclipseJDTCompiler() ) {
- return new EclipseElementUtilsDecorator( processingEnvironment );
+ return new EclipseElementUtilsDecorator( processingEnvironment, mapperElement );
}
else {
- return new JavacElementUtilsDecorator( processingEnvironment );
+ return new JavacElementUtilsDecorator( processingEnvironment, mapperElement );
}
}
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/IgnoreJRERequirement.java b/processor/src/main/java/org/mapstruct/ap/internal/util/IgnoreJRERequirement.java
new file mode 100644
index 000000000..565ebcce1
--- /dev/null
+++ b/processor/src/main/java/org/mapstruct/ap/internal/util/IgnoreJRERequirement.java
@@ -0,0 +1,16 @@
+/*
+ * 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.internal.util;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.CLASS)
+@Target({ ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.TYPE })
+public @interface IgnoreJRERequirement {
+}
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/JavacElementUtilsDecorator.java b/processor/src/main/java/org/mapstruct/ap/internal/util/JavacElementUtilsDecorator.java
index ad035743c..fa0e46171 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/util/JavacElementUtilsDecorator.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/util/JavacElementUtilsDecorator.java
@@ -10,8 +10,8 @@ import javax.lang.model.element.TypeElement;
public class JavacElementUtilsDecorator extends AbstractElementUtilsDecorator {
- JavacElementUtilsDecorator(ProcessingEnvironment processingEnv) {
- super( processingEnv );
+ JavacElementUtilsDecorator(ProcessingEnvironment processingEnv, TypeElement mapperElement) {
+ super( processingEnv, mapperElement );
}
@Override