diff --git a/.github/workflows/default.yml b/.github/workflows/default.yml index 02e7bba3..2d9f8e48 100644 --- a/.github/workflows/default.yml +++ b/.github/workflows/default.yml @@ -13,24 +13,19 @@ jobs: strategy: matrix: os: [ ubuntu-latest,macos-latest,windows-latest ] - java: [ 8.0.x, 9.0.x, 11.0.x, 12.0.x, 13.0.x, 14.0.x, 15.0.x, 16.0.x, 17.0.x ] + java: [ '8', '11', '17', '21' ] steps: - name: Checkout Code - uses: actions/checkout@v1 + uses: actions/checkout@v4 - name: Set up JDK ${{ matrix.java }} - uses: actions/setup-java@v1 + uses: actions/setup-java@v4 with: + distribution: 'zulu' java-version: ${{ matrix.java }} + cache: 'maven' - - name: Cache .m2 - uses: actions/cache@v1 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven - name: Prepare mvnw run: chmod +x ./mvnw @@ -38,3 +33,5 @@ jobs: - name: Build run: ./mvnw clean package + + diff --git a/.github/workflows/default_oracle.yml b/.github/workflows/default_oracle.yml new file mode 100644 index 00000000..d6892c9b --- /dev/null +++ b/.github/workflows/default_oracle.yml @@ -0,0 +1,35 @@ +name: default_oracle + +on: + push: + branches: + - '*' + - '**/*' + - '!master' + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ ubuntu-latest,macos-latest,windows-latest ] + java: [ '17', '21' ] + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Set up JDK ${{ matrix.java }} + uses: actions/setup-java@v4 + with: + distribution: 'oracle' + java-version: ${{ matrix.java }} + cache: 'maven' + + + - name: Prepare mvnw + run: chmod +x ./mvnw + + - name: Build + run: ./mvnw clean package + diff --git a/.github/workflows/default_temurin.yml b/.github/workflows/default_temurin.yml new file mode 100644 index 00000000..04877f37 --- /dev/null +++ b/.github/workflows/default_temurin.yml @@ -0,0 +1,35 @@ +name: default_temurin + +on: + push: + branches: + - '*' + - '**/*' + - '!master' + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ ubuntu-latest,macos-latest,windows-latest ] + java: [ '8', '11', '17', '21' ] + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Set up JDK ${{ matrix.java }} + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: ${{ matrix.java }} + cache: 'maven' + + + - name: Prepare mvnw + run: chmod +x ./mvnw + + - name: Build + run: ./mvnw clean package + diff --git a/.github/workflows/dropStaging.yml b/.github/workflows/dropStaging.yml index dd930de1..7836d736 100644 --- a/.github/workflows/dropStaging.yml +++ b/.github/workflows/dropStaging.yml @@ -17,13 +17,15 @@ jobs: # Steps represent a sequence of tasks that will be executed as part of the job steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 # Setup JDK and Maven - name: Set up JDK 9 - uses: actions/setup-java@v1 + uses: actions/setup-java@v4 with: - java-version: 9 + distribution: 'zulu' + java-version: 17 + cache: 'maven' server-id: sonatype-nexus-staging server-username: OSS_CENTRAL_USERNAME # env variable for Maven Central server-password: OSS_CENTRAL_PASSWORD # env variable for Maven Central diff --git a/.github/workflows/jacoco.yml b/.github/workflows/jacoco.yml index 46456753..9661e777 100644 --- a/.github/workflows/jacoco.yml +++ b/.github/workflows/jacoco.yml @@ -13,20 +13,15 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v1 + uses: actions/checkout@v4 - - name: Set up JDK 9 - uses: actions/setup-java@v1 + - name: Set up JDK 17 + uses: actions/setup-java@v4 with: - java-version: 9 + distribution: 'zulu' + java-version: 17 + cache: 'maven' - - name: Cache .m2 - uses: actions/cache@v1 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven - name: Prepare mvnw run: chmod +x ./mvnw diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6d056bb3..718bbf49 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,15 +11,7 @@ jobs: steps: - name: Checkout Code - uses: actions/checkout@v1 - - - name: Cache .m2 - uses: actions/cache@v1 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven + uses: actions/checkout@v4 # Get GPG private key into GPG - name: Import GPG Owner Trust @@ -28,10 +20,12 @@ jobs: run: echo ${{ secrets.GPG_SECRET_KEYS }} | base64 --decode | gpg --import --no-tty --batch --yes # Setup JDK and Maven - - name: Set up JDK 9 - uses: actions/setup-java@v1 + - name: Set up JDK 17 + uses: actions/setup-java@v4 with: - java-version: 9 + distribution: 'zulu' + java-version: 17 + cache: 'maven' server-id: sonatype-nexus-staging server-username: OSS_CENTRAL_USERNAME # env variable for Maven Central server-password: OSS_CENTRAL_PASSWORD # env variable for Maven Central diff --git a/annotationwrapper/api/pom.xml b/annotationwrapper/api/pom.xml index 0680b126..4119864a 100644 --- a/annotationwrapper/api/pom.xml +++ b/annotationwrapper/api/pom.xml @@ -8,7 +8,7 @@ io.toolisticon.aptk aptk-annotationwrapper-parent - 0.24.1-SNAPSHOT + 0.24.1-148_records-SNAPSHOT aptk-annotationwrapper-api diff --git a/annotationwrapper/integrationtest/pom.xml b/annotationwrapper/integrationtest/pom.xml index 454c3335..c7a5d39c 100644 --- a/annotationwrapper/integrationtest/pom.xml +++ b/annotationwrapper/integrationtest/pom.xml @@ -9,7 +9,7 @@ io.toolisticon.aptk aptk-annotationwrapper-parent - 0.24.1-SNAPSHOT + 0.24.1-148_records-SNAPSHOT aptk-annotationwrapper-integrationTest diff --git a/annotationwrapper/pom.xml b/annotationwrapper/pom.xml index 5068d84e..e4fc4c27 100644 --- a/annotationwrapper/pom.xml +++ b/annotationwrapper/pom.xml @@ -11,7 +11,7 @@ io.toolisticon.aptk aptk-parent - 0.24.1-SNAPSHOT + 0.24.1-148_records-SNAPSHOT diff --git a/annotationwrapper/processor/pom.xml b/annotationwrapper/processor/pom.xml index 5f2055b9..df8ff143 100644 --- a/annotationwrapper/processor/pom.xml +++ b/annotationwrapper/processor/pom.xml @@ -9,7 +9,7 @@ io.toolisticon.aptk aptk-annotationwrapper-parent - 0.24.1-SNAPSHOT + 0.24.1-148_records-SNAPSHOT aptk-annotationwrapper-processor diff --git a/common/pom.xml b/common/pom.xml index 0b3c3313..cd1d67d8 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -7,7 +7,7 @@ io.toolisticon.aptk aptk-parent - 0.24.1-SNAPSHOT + 0.24.1-148_records-SNAPSHOT aptk-common diff --git a/compilermessages/api/pom.xml b/compilermessages/api/pom.xml index 4173655a..ff64ba8a 100644 --- a/compilermessages/api/pom.xml +++ b/compilermessages/api/pom.xml @@ -8,7 +8,7 @@ io.toolisticon.aptk aptk-compilermessages-parent - 0.24.1-SNAPSHOT + 0.24.1-148_records-SNAPSHOT aptk-compilermessages-api diff --git a/compilermessages/integrationtest/pom.xml b/compilermessages/integrationtest/pom.xml index 1eb7e855..51b56485 100644 --- a/compilermessages/integrationtest/pom.xml +++ b/compilermessages/integrationtest/pom.xml @@ -9,7 +9,7 @@ io.toolisticon.aptk aptk-compilermessages-parent - 0.24.1-SNAPSHOT + 0.24.1-148_records-SNAPSHOT aptk-compilermessages-integrationTest diff --git a/compilermessages/pom.xml b/compilermessages/pom.xml index 265d741c..ce55730c 100644 --- a/compilermessages/pom.xml +++ b/compilermessages/pom.xml @@ -11,7 +11,7 @@ io.toolisticon.aptk aptk-parent - 0.24.1-SNAPSHOT + 0.24.1-148_records-SNAPSHOT diff --git a/compilermessages/processor/pom.xml b/compilermessages/processor/pom.xml index b3818320..3df8978b 100644 --- a/compilermessages/processor/pom.xml +++ b/compilermessages/processor/pom.xml @@ -9,7 +9,7 @@ io.toolisticon.aptk aptk-compilermessages-parent - 0.24.1-SNAPSHOT + 0.24.1-148_records-SNAPSHOT aptk-compilermessages-processor diff --git a/cute/pom.xml b/cute/pom.xml index 5ea81dbb..ec79516d 100644 --- a/cute/pom.xml +++ b/cute/pom.xml @@ -8,7 +8,7 @@ io.toolisticon.aptk aptk-parent - 0.24.1-SNAPSHOT + 0.24.1-148_records-SNAPSHOT aptk-cute diff --git a/example/example-annotationprocessor/pom.xml b/example/example-annotationprocessor/pom.xml index f7e345ec..b54fd042 100644 --- a/example/example-annotationprocessor/pom.xml +++ b/example/example-annotationprocessor/pom.xml @@ -8,7 +8,7 @@ io.toolisticon.aptk aptk-example-parent - 0.24.1-SNAPSHOT + 0.24.1-148_records-SNAPSHOT aptk-example-annotationprocessor diff --git a/example/example-annotations/pom.xml b/example/example-annotations/pom.xml index a77b9fc6..5af61772 100644 --- a/example/example-annotations/pom.xml +++ b/example/example-annotations/pom.xml @@ -7,7 +7,7 @@ io.toolisticon.aptk aptk-example-parent - 0.24.1-SNAPSHOT + 0.24.1-148_records-SNAPSHOT aptk-example-annotations diff --git a/example/example-usecase/pom.xml b/example/example-usecase/pom.xml index 2de146b6..8e24038d 100644 --- a/example/example-usecase/pom.xml +++ b/example/example-usecase/pom.xml @@ -7,7 +7,7 @@ io.toolisticon.aptk aptk-example-parent - 0.24.1-SNAPSHOT + 0.24.1-148_records-SNAPSHOT aptk-example-usecase diff --git a/example/pom.xml b/example/pom.xml index 63ce5bc6..a5069553 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -7,7 +7,7 @@ io.toolisticon.aptk aptk-parent - 0.24.1-SNAPSHOT + 0.24.1-148_records-SNAPSHOT aptk-example-parent diff --git a/extensions/java9/src/main/java/io/toolisticon/aptk/tools/wrapper/ModuleElementWrapper.java b/extensions/java9/src/main/java/io/toolisticon/aptk/tools/wrapper/ModuleElementWrapper.java deleted file mode 100644 index aeea5432..00000000 --- a/extensions/java9/src/main/java/io/toolisticon/aptk/tools/wrapper/ModuleElementWrapper.java +++ /dev/null @@ -1,47 +0,0 @@ -package io.toolisticon.aptk.tools.wrapper; - -import javax.lang.model.element.Element; -import javax.lang.model.element.ElementKind; -import javax.lang.model.element.ModuleElement; -import java.util.List; -import java.util.stream.Collectors; - -public class ModuleElementWrapper extends ElementWrapper { - - private ModuleElementWrapper(ModuleElement moduleElement) { - super(moduleElement); - } - - public String getQualifiedName() { - return this.element.getQualifiedName().toString(); - } - - public boolean hasQualifiedName(String name) { - return name != null && this.getQualifiedName().equals(name); - } - - public boolean isOpen() { - return this.element.isOpen(); - } - - public boolean isUnnamed() { - return this.element.isUnnamed(); - } - - public List getDirectives() { - return this.element.getDirectives(); - } - - public static ModuleElementWrapper toModuleElement(Element element) { - return ModuleElementWrapper.wrap((ModuleElement) element); - } - - public static ModuleElementWrapper wrap(ModuleElement moduleElement) { - return new ModuleElementWrapper(moduleElement); - } - - public static List wrapList(List moduleElements) { - return moduleElements.stream().map(ModuleElementWrapper::new).collect(Collectors.toList()); - } - -} diff --git a/extensions/java9/src/test/java/io/toolisticon/aptk/wrapper/ModuleElementWrapperTest.java b/extensions/java9/src/test/java/io/toolisticon/aptk/wrapper/ModuleElementWrapperTest.java deleted file mode 100644 index 406c28b2..00000000 --- a/extensions/java9/src/test/java/io/toolisticon/aptk/wrapper/ModuleElementWrapperTest.java +++ /dev/null @@ -1,111 +0,0 @@ -package io.toolisticon.aptk.wrapper; - -import io.toolisticon.aptk.tools.wrapper.ElementWrapper; -import io.toolisticon.aptk.tools.wrapper.ModuleElementWrapper; -import org.hamcrest.MatcherAssert; -import org.hamcrest.Matchers; -import org.junit.Test; -import org.mockito.Mockito; - -import javax.lang.model.element.ElementKind; -import javax.lang.model.element.ModuleElement; -import javax.lang.model.element.Name; -import javax.lang.model.element.TypeElement; - -/** - * Unit test for {@link io.toolisticon.aptk.tools.wrapper.ModuleElementWrapper}. - */ -public class ModuleElementWrapperTest { - - - @Test - public void testCreationOfWrapperAndUnwrap() { - ModuleElement moduleElement = Mockito.mock(ModuleElement.class); - ModuleElementWrapper unit = ModuleElementWrapper.wrap(moduleElement); - MatcherAssert.assertThat(unit, Matchers.notNullValue()); - MatcherAssert.assertThat(unit.unwrap(), Matchers.is(moduleElement)); - } - - @Test - public void test_getQualifiedName() { - ModuleElement moduleElement = Mockito.mock(ModuleElement.class); - Name name = Mockito.mock(Name.class); - Mockito.when(name.toString()).thenReturn("JUPP"); - Mockito.when(moduleElement.getQualifiedName()).thenReturn(name); - ModuleElementWrapper unit = ModuleElementWrapper.wrap(moduleElement); - MatcherAssert.assertThat(unit.getQualifiedName(), Matchers.is("JUPP")); - } - - @Test - public void test_hasQualifiedName() { - ModuleElement moduleElement = Mockito.mock(ModuleElement.class); - Name name = Mockito.mock(Name.class); - Mockito.when(name.toString()).thenReturn("YES"); - Mockito.when(moduleElement.getQualifiedName()).thenReturn(name); - ModuleElementWrapper unit = ModuleElementWrapper.wrap(moduleElement); - MatcherAssert.assertThat(unit.hasQualifiedName("YES"), Matchers.is(true)); - MatcherAssert.assertThat(unit.hasQualifiedName("NO"), Matchers.is(false)); - MatcherAssert.assertThat(unit.hasQualifiedName(null), Matchers.is(false)); - } - - @Test - public void proxyTests_isOpen() { - ModuleElement moduleElement = Mockito.spy(ModuleElement.class); - ModuleElementWrapper unit = ModuleElementWrapper.wrap(moduleElement); - - unit.isOpen(); - Mockito.verify(moduleElement, Mockito.times(1)).isOpen(); - - } - - @Test - public void proxyTests_isUnnamed() { - ModuleElement moduleElement = Mockito.spy(ModuleElement.class); - ModuleElementWrapper unit = ModuleElementWrapper.wrap(moduleElement); - - unit.isUnnamed(); - Mockito.verify(moduleElement, Mockito.times(1)).isUnnamed(); - - } - - @Test - public void proxyTests_getDirectives() { - ModuleElement moduleElement = Mockito.spy(ModuleElement.class); - ModuleElementWrapper unit = ModuleElementWrapper.wrap(moduleElement); - - unit.getDirectives(); - Mockito.verify(moduleElement, Mockito.times(1)).getDirectives(); - - } - - @Test - public void test_isModuleElement() { - ModuleElement moduleElement = Mockito.mock(ModuleElement.class); - - Mockito.when(moduleElement.getKind()).thenReturn(ElementKind.MODULE); - MatcherAssert.assertThat(ElementWrapper.wrap(moduleElement).isModule(), Matchers.is(true)); - - - TypeElement typeElement = Mockito.mock(TypeElement.class); - Mockito.when(typeElement.getKind()).thenReturn(ElementKind.CLASS); - MatcherAssert.assertThat(ElementWrapper.wrap(typeElement).isModuleElement(), Matchers.is(false)); - - } -/*- - - public static boolean isModuleElement(Element element) { - return element != null & element.getKind().equals(ElementKind.MODULE); - } - - public static ModuleElementWrapperTest toModuleElement(Element element) { - return ModuleElementWrapperTest.wrap((ModuleElement) element); - } - - public static ModuleElementWrapperTest wrap(ModuleElement moduleElement) { - return new ModuleElementWrapperTest(moduleElement); - } - - - */ - -} diff --git a/extensions/pom.xml b/extensions/pom.xml deleted file mode 100644 index 5e41e6c1..00000000 --- a/extensions/pom.xml +++ /dev/null @@ -1,31 +0,0 @@ - - 4.0.0 - - extension-parent - pom - - - io.toolisticon.aptk - aptk-parent - 0.24.1-SNAPSHOT - - - extension-parent - - - - - - java-9 - - [9,) - - - - java9 - - - - - - diff --git a/integrationtest/java16/pom.xml b/integrationtest/java16/pom.xml new file mode 100644 index 00000000..9c9e9b22 --- /dev/null +++ b/integrationtest/java16/pom.xml @@ -0,0 +1,94 @@ + + 4.0.0 + + aptk-tools-java16 + jar + + + io.toolisticon.aptk + integrationtest-parent + 0.24.1-148_records-SNAPSHOT + + + aptk-tools-java16 + + + + + + io.toolisticon.aptk + aptk-tools + + + + + + + + + + + maven-enforcer-plugin + + + enforce + + enforce + + + + + [3.0.4,) + + + 16 + + + false + + * + + + io.toolisticon.aptk:aptk-tools:* + *:*:*:*:test:* + *:*:*:*:provided:* + + + + + + + + + + maven-compiler-plugin + + 16 + 16 + + + + + org.codehaus.mojo + animal-sniffer-maven-plugin + + true + + + + + org.apache.maven.plugins + maven-deploy-plugin + + true + + + + + + + + + + diff --git a/integrationtest/java16/src/test/java/io/toolisticon/aptk/tools/wrapper/Java16Tests.java b/integrationtest/java16/src/test/java/io/toolisticon/aptk/tools/wrapper/Java16Tests.java new file mode 100644 index 00000000..6bb9b3d8 --- /dev/null +++ b/integrationtest/java16/src/test/java/io/toolisticon/aptk/tools/wrapper/Java16Tests.java @@ -0,0 +1,171 @@ +package io.toolisticon.aptk.tools.wrapper; + +import io.toolisticon.aptk.common.ToolingProvider; +import io.toolisticon.aptk.tools.corematcher.AptkCoreMatchers; +import io.toolisticon.cute.Cute; +import io.toolisticon.cute.PassIn; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.Test; + +import javax.lang.model.element.TypeElement; +import java.util.Set; +import java.util.stream.Collectors; + +public class Java16Tests { + + @PassIn + record MyRecord(String name, String surname) { + } + + @Test + public void test_records_isRecord() { + Cute.unitTest().when().passInElement().fromClass(MyRecord.class).intoUnitTest((processingEnvironment, element) -> { + try { + ToolingProvider.setTooling(processingEnvironment); + + ElementWrapper elementWrapper = ElementWrapper.wrap(element); + + MatcherAssert.assertThat("Should detect record ", elementWrapper.isRecord()); + MatcherAssert.assertThat("Should not detect record ", !TypeElementWrapper.getByClass(Java16Tests.class).get().isRecord()); + + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationSucceeds().executeTest(); + + } + + @Test + public void test_records_isTypeElement() { + Cute.unitTest().when().passInElement().fromClass(MyRecord.class).intoUnitTest((processingEnvironment, element) -> { + try { + ToolingProvider.setTooling(processingEnvironment); + + ElementWrapper elementWrapper = ElementWrapper.wrap(element); + + MatcherAssert.assertThat("Should detect record ", elementWrapper.isTypeElement()); + MatcherAssert.assertThat("Should return false", !elementWrapper.isExecutableElement()); + MatcherAssert.assertThat("Should return false ", !elementWrapper.isVariableElement()); + MatcherAssert.assertThat("Should return false ", !elementWrapper.isTypeParameterElement()); + MatcherAssert.assertThat("Should return false", !elementWrapper.isModuleElement()); + + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationSucceeds().executeTest(); + + } + + + @Test + public void test_recordComponent_isRecordComponent() { + Cute.unitTest().when().passInElement().fromClass(MyRecord.class).intoUnitTest((processingEnvironment, element) -> { + try { + ToolingProvider.setTooling(processingEnvironment); + + TypeElement typeElement = (TypeElement) element; + + ElementWrapper elementWrapper = ElementWrapper.wrap(typeElement.getRecordComponents().get(0)); + + MatcherAssert.assertThat("Should detect record ", elementWrapper.isRecordComponent()); + MatcherAssert.assertThat("Should not detect record ", !TypeElementWrapper.getByClass(Java16Tests.class).get().isRecordComponent()); + + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationSucceeds().executeTest(); + + } + + @Test + public void test_recordComponent_isTypeElement() { + Cute.unitTest().when().passInElement().fromClass(MyRecord.class).intoUnitTest((processingEnvironment, element) -> { + try { + ToolingProvider.setTooling(processingEnvironment); + + TypeElement typeElement = (TypeElement) element; + + ElementWrapper elementWrapper = ElementWrapper.wrap(typeElement.getRecordComponents().get(0)); + + MatcherAssert.assertThat("Should detect RecordComponentElement ", elementWrapper.isRecordComponentElement()); + MatcherAssert.assertThat("Should detect false ", !elementWrapper.isTypeElement()); + MatcherAssert.assertThat("Should return false", !elementWrapper.isExecutableElement()); + MatcherAssert.assertThat("Should return false ", !elementWrapper.isVariableElement()); + MatcherAssert.assertThat("Should return false ", !elementWrapper.isTypeParameterElement()); + MatcherAssert.assertThat("Should return false", !elementWrapper.isModuleElement()); + + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationSucceeds().executeTest(); + + } + + @Test + public void test_recordComponent_accessWrapperMethods() { + Cute.unitTest().when().passInElement().fromClass(MyRecord.class).intoUnitTest((processingEnvironment, element) -> { + try { + ToolingProvider.setTooling(processingEnvironment); + TypeElementWrapper typeElement = TypeElementWrapper.wrap(element); + + RecordComponentElementWrapper elementWrapper = typeElement.getRecordComponents().get(0); + MatcherAssert.assertThat(elementWrapper.getSimpleName(), Matchers.is("name")); + MatcherAssert.assertThat("Must be methods", elementWrapper.getAccessor().isMethod()); + MatcherAssert.assertThat(elementWrapper.getEnclosingRecordTypeElement().getQualifiedName(), Matchers.is(MyRecord.class.getCanonicalName())); + MatcherAssert.assertThat(TypeElementWrapper.toTypeElement(elementWrapper.getEnclosingElement().get()).getQualifiedName(), Matchers.is(MyRecord.class.getCanonicalName())); + + + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationSucceeds().executeTest(); + + } + + + @Test + public void test_recordComponent_filtering_byTypeElement() { + Cute.unitTest().when((processingEnvironment) -> { + try { + ToolingProvider.setTooling(processingEnvironment); + TypeElementWrapper typeElement = TypeElementWrapper.getByClass(Java16Tests.class).get(); + + Set enclosedTypeElements = typeElement.filterFlattenedEnclosedElementTree().applyFilter(AptkCoreMatchers.IS_TYPE_ELEMENT).getResult().stream().map(e -> e.getQualifiedName().toString()).collect(Collectors.toSet()); + + MatcherAssert.assertThat(enclosedTypeElements, Matchers.contains(MyRecord.class.getCanonicalName())); + + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationSucceeds().executeTest(); + + } + + @Test + public void test_recordComponent_filtering_byRecord() { + Cute.unitTest().when((processingEnvironment) -> { + try { + ToolingProvider.setTooling(processingEnvironment); + TypeElementWrapper typeElement = TypeElementWrapper.getByClass(Java16Tests.class).get(); + + Set enclosedTypeElements = typeElement.filterFlattenedEnclosedElementTree().applyFilter(AptkCoreMatchers.IS_RECORD).getResult().stream().map(e -> e.getQualifiedName().toString()).collect(Collectors.toSet()); + + MatcherAssert.assertThat(enclosedTypeElements, Matchers.hasSize(1)); + MatcherAssert.assertThat(enclosedTypeElements, Matchers.contains(MyRecord.class.getCanonicalName())); + + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationSucceeds().executeTest(); + + } + +} diff --git a/integrationtest/java17/pom.xml b/integrationtest/java17/pom.xml new file mode 100644 index 00000000..a5c9c710 --- /dev/null +++ b/integrationtest/java17/pom.xml @@ -0,0 +1,94 @@ + + 4.0.0 + + aptk-tools-java17 + jar + + + io.toolisticon.aptk + integrationtest-parent + 0.24.1-148_records-SNAPSHOT + + + aptk-tools-java17 + + + + + + io.toolisticon.aptk + aptk-tools + + + + + + + + + + + maven-enforcer-plugin + + + enforce + + enforce + + + + + [3.0.4,) + + + 17 + + + false + + * + + + io.toolisticon.aptk:aptk-tools:* + *:*:*:*:test:* + *:*:*:*:provided:* + + + + + + + + + + maven-compiler-plugin + + 17 + 17 + + + + + org.codehaus.mojo + animal-sniffer-maven-plugin + + true + + + + + org.apache.maven.plugins + maven-deploy-plugin + + true + + + + + + + + + + diff --git a/integrationtest/java17/src/test/java/io/toolisticon/aptk/tools/wrapper/Java17Tests.java b/integrationtest/java17/src/test/java/io/toolisticon/aptk/tools/wrapper/Java17Tests.java new file mode 100644 index 00000000..c78b9c17 --- /dev/null +++ b/integrationtest/java17/src/test/java/io/toolisticon/aptk/tools/wrapper/Java17Tests.java @@ -0,0 +1,66 @@ +package io.toolisticon.aptk.tools.wrapper; + +import io.toolisticon.aptk.common.ToolingProvider; +import io.toolisticon.cute.Cute; +import io.toolisticon.cute.PassIn; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.Test; + +import javax.lang.model.element.TypeElement; +import java.util.stream.Collectors; + +public class Java17Tests { + + + @Test + public void test_sealedClassesFeature_unsealed() { + Cute.unitTest().when((processingEnvironment) -> { + try { + ToolingProvider.setTooling(processingEnvironment); + + TypeElementWrapper elementWrapper = TypeElementWrapper.getByClass(Java17Tests.class).get(); + + MatcherAssert.assertThat(elementWrapper.getPermittedSubclasses(), Matchers.empty()); + MatcherAssert.assertThat(elementWrapper.unwrap().getPermittedSubclasses(), Matchers.empty()); + + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationSucceeds().executeTest(); + + } + + + static final class AllowedClass extends SealedClass { + + } + + @PassIn + static sealed class SealedClass permits AllowedClass { + + } + + + @Test + public void test_sealedClassesFeature_sealed() { + Cute.unitTest().when().passInElement().fromClass(SealedClass.class).intoUnitTest((processingEnvironment, element) -> { + try { + ToolingProvider.setTooling(processingEnvironment); + + TypeElementWrapper elementWrapper = TypeElementWrapper.wrap(element); + + MatcherAssert.assertThat(elementWrapper.getPermittedSubclasses().stream().map(e -> e.getQualifiedName()).collect(Collectors.toSet()), Matchers.contains(AllowedClass.class.getCanonicalName())); + MatcherAssert.assertThat(element.getPermittedSubclasses().stream().map(e -> e.toString()).collect(Collectors.toSet()), Matchers.contains(AllowedClass.class.getCanonicalName())); + + } finally { + ToolingProvider.clearTooling(); + } + + }).thenExpectThat().compilationSucceeds().executeTest(); + + } + + +} diff --git a/extensions/java9/pom.xml b/integrationtest/java9/pom.xml similarity index 88% rename from extensions/java9/pom.xml rename to integrationtest/java9/pom.xml index b856cd97..761d794e 100644 --- a/extensions/java9/pom.xml +++ b/integrationtest/java9/pom.xml @@ -7,8 +7,8 @@ io.toolisticon.aptk - extension-parent - 0.24.1-SNAPSHOT + integrationtest-parent + 0.24.1-148_records-SNAPSHOT aptk-tools-java9 @@ -77,6 +77,13 @@ + + org.apache.maven.plugins + maven-deploy-plugin + + true + + diff --git a/integrationtest/java9/src/test/java/io/toolisticon/aptk/wrapper/ModuleElementWrapperTest.java b/integrationtest/java9/src/test/java/io/toolisticon/aptk/wrapper/ModuleElementWrapperTest.java new file mode 100644 index 00000000..595362ab --- /dev/null +++ b/integrationtest/java9/src/test/java/io/toolisticon/aptk/wrapper/ModuleElementWrapperTest.java @@ -0,0 +1,179 @@ +package io.toolisticon.aptk.wrapper; + +import io.toolisticon.aptk.common.ToolingProvider; +import io.toolisticon.aptk.tools.wrapper.ElementWrapper; +import io.toolisticon.aptk.tools.wrapper.ModuleElementWrapper; +import io.toolisticon.aptk.tools.wrapper.TypeElementWrapper; +import io.toolisticon.cute.Cute; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.Test; +import org.mockito.Mockito; + +import javax.annotation.processing.Processor; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ModuleElement; +import javax.lang.model.element.Name; +import javax.lang.model.element.TypeElement; +import java.util.Optional; + +/** + * Unit test for {@link io.toolisticon.aptk.tools.wrapper.ModuleElementWrapper}. + */ +public class ModuleElementWrapperTest { + + + @Test + public void testCreationOfWrapperAndUnwrap() { + ModuleElement moduleElement = Mockito.mock(ModuleElement.class); + Mockito.when(moduleElement.getKind()).thenReturn(ElementKind.MODULE); + + ModuleElementWrapper unit = ModuleElementWrapper.wrap((Element) moduleElement); + MatcherAssert.assertThat(unit, Matchers.notNullValue()); + MatcherAssert.assertThat(unit.unwrap(), Matchers.is(moduleElement)); + } + + @Test + public void test_getQualifiedName() { + ModuleElement moduleElement = Mockito.mock(ModuleElement.class); + Mockito.when(moduleElement.getKind()).thenReturn(ElementKind.MODULE); + + Name name = Mockito.mock(Name.class); + Mockito.when(name.toString()).thenReturn("JUPP"); + Mockito.when(moduleElement.getQualifiedName()).thenReturn(name); + ModuleElementWrapper unit = ModuleElementWrapper.wrap((Element) moduleElement); + MatcherAssert.assertThat(unit.getQualifiedName(), Matchers.is("JUPP")); + } + + @Test + public void test_hasQualifiedName() { + ModuleElement moduleElement = Mockito.mock(ModuleElement.class); + Mockito.when(moduleElement.getKind()).thenReturn(ElementKind.MODULE); + + Name name = Mockito.mock(Name.class); + Mockito.when(name.toString()).thenReturn("YES"); + Mockito.when(moduleElement.getQualifiedName()).thenReturn(name); + ModuleElementWrapper unit = ModuleElementWrapper.wrap((Element) moduleElement); + MatcherAssert.assertThat(unit.hasQualifiedName("YES"), Matchers.is(true)); + MatcherAssert.assertThat(unit.hasQualifiedName("NO"), Matchers.is(false)); + MatcherAssert.assertThat(unit.hasQualifiedName(null), Matchers.is(false)); + } + + @Test + public void proxyTests_isOpen() { + ModuleElement moduleElement = Mockito.spy(ModuleElement.class); + Mockito.when(moduleElement.getKind()).thenReturn(ElementKind.MODULE); + + ModuleElementWrapper unit = ModuleElementWrapper.wrap((Element) moduleElement); + + unit.isOpen(); + Mockito.verify(moduleElement, Mockito.times(1)).isOpen(); + + } + + @Test + public void proxyTests_isUnnamed() { + ModuleElement moduleElement = Mockito.spy(ModuleElement.class); + Mockito.when(moduleElement.getKind()).thenReturn(ElementKind.MODULE); + + ModuleElementWrapper unit = ModuleElementWrapper.wrap((Element) moduleElement); + + unit.isUnnamed(); + Mockito.verify(moduleElement, Mockito.times(1)).isUnnamed(); + + } + + /*- + // TODO: MUST IMPLEMENT DIRECTIVES VIA REFLECTION + @Test + public void proxyTests_getDirectives() { + ModuleElement moduleElement = Mockito.spy(ModuleElement.class); + ModuleElementWrapper unit = ModuleElementWrapper.wrap((Element)moduleElement); + + unit.getDirectives(); + Mockito.verify(moduleElement, Mockito.times(1)).getDirectives(); + + } + */ + + @Test + public void test_isModuleElement() { + ModuleElement moduleElement = Mockito.mock(ModuleElement.class); + Mockito.when(moduleElement.getKind()).thenReturn(ElementKind.MODULE); + + MatcherAssert.assertThat(ElementWrapper.wrap(moduleElement).isModule(), Matchers.is(true)); + + + TypeElement typeElement = Mockito.mock(TypeElement.class); + Mockito.when(typeElement.getKind()).thenReturn(ElementKind.CLASS); + MatcherAssert.assertThat(ElementWrapper.wrap(typeElement).isModuleElement(), Matchers.is(false)); + + } + + + @Test + public void test_blackbox_ModuleElementWrapper_variousTests() { + Cute.unitTest().given().useSourceFiles("/testcases/namedModule/module-info.java", "/testcases/namedModule/TestClass.java", "/testcases/namedModule/MyProcessor.java") + .when().unitTestWithoutPassIn(processingEnvironment -> { + try { + ToolingProvider.setTooling(processingEnvironment); + TypeElementWrapper typeElementWrapper = TypeElementWrapper.getByFqn("io.toolisticon.aptk.example.regularmodule.TestClass").get(); + Optional moduleElementWrapper = typeElementWrapper.getModule(); + MatcherAssert.assertThat("Should find module", moduleElementWrapper.isPresent()); + MatcherAssert.assertThat("Should be true", moduleElementWrapper.get().isModuleElement()); + + MatcherAssert.assertThat(moduleElementWrapper.get().getQualifiedName(), Matchers.is("io.toolisticon.aptk.example")); + MatcherAssert.assertThat(moduleElementWrapper.get().getDirectives(), Matchers.hasSize(7)); + + // default: requires java.base + MatcherAssert.assertThat("Should be true", moduleElementWrapper.get().getDirectives().get(0).isRequiresDirective()); + MatcherAssert.assertThat("Should be false for non static", !moduleElementWrapper.get().getDirectives().get(0).toRequiresDirective().isStatic()); + MatcherAssert.assertThat("Should be false for non transitive", !moduleElementWrapper.get().getDirectives().get(0).toRequiresDirective().isTransitive()); + MatcherAssert.assertThat(moduleElementWrapper.get().getDirectives().get(0).toRequiresDirective().getDependency().getQualifiedName(), Matchers.is("java.base")); + + // exports io.toolisticon.aptk.example.regularmodule; + MatcherAssert.assertThat("Should be true", moduleElementWrapper.get().getDirectives().get(1).isExportDirective()); + MatcherAssert.assertThat(moduleElementWrapper.get().getDirectives().get(1).toExportsDirective().getPackage().getQualifiedName(), Matchers.is("io.toolisticon.aptk.example.regularmodule")); + MatcherAssert.assertThat(moduleElementWrapper.get().getDirectives().get(1).toExportsDirective().getTargetModules(), Matchers.hasSize(1)); + MatcherAssert.assertThat(moduleElementWrapper.get().getDirectives().get(1).toExportsDirective().getTargetModules().get(0).getQualifiedName(), Matchers.is("cute")); + + // requires cute + MatcherAssert.assertThat("Should be true", moduleElementWrapper.get().getDirectives().get(2).isRequiresDirective()); + MatcherAssert.assertThat("Should be false for non static", !moduleElementWrapper.get().getDirectives().get(2).toRequiresDirective().isStatic()); + MatcherAssert.assertThat("Should be true for non transitive", moduleElementWrapper.get().getDirectives().get(2).toRequiresDirective().isTransitive()); + MatcherAssert.assertThat(moduleElementWrapper.get().getDirectives().get(2).toRequiresDirective().getDependency().getQualifiedName(), Matchers.is("cute")); + + // requires jdk.compiler; + MatcherAssert.assertThat("Should be true", moduleElementWrapper.get().getDirectives().get(3).isRequiresDirective()); + MatcherAssert.assertThat("Should be true for non static", moduleElementWrapper.get().getDirectives().get(3).toRequiresDirective().isStatic()); + MatcherAssert.assertThat("Should be false for non transitive", !moduleElementWrapper.get().getDirectives().get(3).toRequiresDirective().isTransitive()); + MatcherAssert.assertThat(moduleElementWrapper.get().getDirectives().get(3).toRequiresDirective().getDependency().getQualifiedName(), Matchers.is("jdk.compiler")); + + // uses Processor + MatcherAssert.assertThat("Should be true", moduleElementWrapper.get().getDirectives().get(4).isUsesDirective()); + MatcherAssert.assertThat(moduleElementWrapper.get().getDirectives().get(4).toUsesDirective().getService().getQualifiedName(), Matchers.is(Processor.class.getCanonicalName())); + + // opens io.toolisticon.aptk.example.regularmodule to cute + MatcherAssert.assertThat("Should be true", moduleElementWrapper.get().getDirectives().get(5).isOpensDirective()); + MatcherAssert.assertThat(moduleElementWrapper.get().getDirectives().get(5).toOpensDirective().getPackage().getQualifiedName(), Matchers.is("io.toolisticon.aptk.example.regularmodule")); + MatcherAssert.assertThat(moduleElementWrapper.get().getDirectives().get(5).toOpensDirective().getTargetModules().get(0).getQualifiedName(), Matchers.is("cute")); + + // provides Processor with io.toolisticon.aptk.example.regularmodule.MyProcessor; + MatcherAssert.assertThat("Should be true", moduleElementWrapper.get().getDirectives().get(6).isProvidesDirective()); + MatcherAssert.assertThat(moduleElementWrapper.get().getDirectives().get(6).toProvidesDirective().getService().getQualifiedName(), Matchers.is(Processor.class.getCanonicalName())); + MatcherAssert.assertThat(moduleElementWrapper.get().getDirectives().get(6).toProvidesDirective().getImplementations().get(0).getQualifiedName(), Matchers.is("io.toolisticon.aptk.example.regularmodule.MyProcessor")); + + + } finally { + ToolingProvider.clearTooling(); + } + }) + .thenExpectThat() + .compilationSucceeds() + .executeTest(); + + } + + +} diff --git a/integrationtest/java9/src/test/resources/testcases/namedModule/MyProcessor.java b/integrationtest/java9/src/test/resources/testcases/namedModule/MyProcessor.java new file mode 100644 index 00000000..137d7f5c --- /dev/null +++ b/integrationtest/java9/src/test/resources/testcases/namedModule/MyProcessor.java @@ -0,0 +1,15 @@ +package io.toolisticon.aptk.example.regularmodule; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.RoundEnvironment; +import javax.lang.model.element.TypeElement; +import java.util.Set; + +public class MyProcessor extends AbstractProcessor { + + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + return false; + } +} \ No newline at end of file diff --git a/integrationtest/java9/src/test/resources/testcases/namedModule/TestClass.java b/integrationtest/java9/src/test/resources/testcases/namedModule/TestClass.java new file mode 100644 index 00000000..462a35ac --- /dev/null +++ b/integrationtest/java9/src/test/resources/testcases/namedModule/TestClass.java @@ -0,0 +1,8 @@ +package io.toolisticon.aptk.example.regularmodule; + +import io.toolisticon.cute.PassIn; + +@PassIn +public class TestClass{ + +} \ No newline at end of file diff --git a/integrationtest/java9/src/test/resources/testcases/namedModule/module-info.java b/integrationtest/java9/src/test/resources/testcases/namedModule/module-info.java new file mode 100644 index 00000000..35ebfde5 --- /dev/null +++ b/integrationtest/java9/src/test/resources/testcases/namedModule/module-info.java @@ -0,0 +1,12 @@ +import javax.annotation.processing.Processor; + +module io.toolisticon.aptk.example { + + exports io.toolisticon.aptk.example.regularmodule to cute; + requires transitive cute; + requires static jdk.compiler; + uses Processor; + opens io.toolisticon.aptk.example.regularmodule to cute; + provides Processor with io.toolisticon.aptk.example.regularmodule.MyProcessor; + +} \ No newline at end of file diff --git a/integrationtest/pom.xml b/integrationtest/pom.xml new file mode 100644 index 00000000..a168efd2 --- /dev/null +++ b/integrationtest/pom.xml @@ -0,0 +1,56 @@ + + 4.0.0 + + integrationtest-parent + pom + + + io.toolisticon.aptk + aptk-parent + 0.24.1-148_records-SNAPSHOT + + + integrationtest-parent + + + + + + java-9 + + [9,) + + + + java9 + + + + + java-16 + + [16,) + + + + java9 + java16 + + + + + java-17 + + [17,) + + + + java9 + java16 + java17 + + + + + + diff --git a/pom.xml b/pom.xml index 78407101..cacf3f49 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ io.toolisticon.aptk aptk-parent - 0.24.1-SNAPSHOT + 0.24.1-148_records-SNAPSHOT pom aptk-parent @@ -18,7 +18,7 @@ tools example templating - extensions + integrationtest annotationwrapper @@ -58,11 +58,6 @@ 2017 - - 3.6.0 - - - https://github.com/toolisticon/aptk/issues GitHub Issues @@ -87,12 +82,13 @@ 0.11.0 - 1.0.0_RC1 + 1.7.0 4.13.2 2.2 - 4.3.1 + 4.11.0 + 5.11.0 https://oss.sonatype.org/content/repositories/snapshots/ @@ -202,18 +198,6 @@ - - org.codehaus.mojo - build-helper-maven-plugin - ${maven-build-helper-plugin.version} - - - - - org.apache.maven.plugins - maven-source-plugin - ${maven-source-plugin.version} - com.amashchenko.maven.plugin @@ -559,6 +543,19 @@ [1.8,9) + + + + + org.mockito + mockito-core + ${mockito.version} + test + + + + + @@ -573,7 +570,7 @@ - check_java17 + check_java8 test @@ -611,9 +608,22 @@ jdkgreaterthan8 - [9,99) + [9,11) + + + + + org.mockito + mockito-core + ${mockito.version} + test + + + + + @@ -671,6 +681,82 @@ + + jdkgreaterthan11 + + [11,99) + + + + + + + org.mockito + mockito-core + ${mockito_jdk11.version} + test + + + + + + + + + + + + + org.codehaus.mojo + animal-sniffer-maven-plugin + 1.18 + + + + com.sun.source.util.Trees + com.sun.source.tree.* + + + + + + + + check_java18 + + test + + + check + + + + + io.toolisticon.cute:* + + + + org.codehaus.mojo.signature + java18 + 1.0 + + + + + + + + + + + + + + + + + + @@ -689,8 +775,6 @@ - - junit @@ -709,7 +793,6 @@ org.mockito mockito-core - ${mockito.version} test @@ -760,6 +843,18 @@ ${project.version} + + io.toolisticon.aptk + aptk-tools-java9 + ${project.version} + + + + io.toolisticon.aptk + aptk-tools-java16 + ${project.version} + + io.toolisticon.spiap spiap-api diff --git a/templating/pom.xml b/templating/pom.xml index 64951be1..8d0fe47c 100644 --- a/templating/pom.xml +++ b/templating/pom.xml @@ -7,7 +7,7 @@ io.toolisticon.aptk aptk-parent - 0.24.1-SNAPSHOT + 0.24.1-148_records-SNAPSHOT aptk-templating diff --git a/tools/pom.xml b/tools/pom.xml index f7164e7a..db1d866d 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -8,7 +8,7 @@ io.toolisticon.aptk aptk-parent - 0.24.1-SNAPSHOT + 0.24.1-148_records-SNAPSHOT aptk-tools @@ -100,6 +100,7 @@ + system ${java.home}/../Classes/classes.jar - + --> diff --git a/tools/src/main/java/io/toolisticon/aptk/tools/BeanUtils.java b/tools/src/main/java/io/toolisticon/aptk/tools/BeanUtils.java index dc0cb27f..2229dab3 100644 --- a/tools/src/main/java/io/toolisticon/aptk/tools/BeanUtils.java +++ b/tools/src/main/java/io/toolisticon/aptk/tools/BeanUtils.java @@ -1,6 +1,5 @@ package io.toolisticon.aptk.tools; -import com.sun.source.tree.StatementTree; import io.toolisticon.aptk.tools.command.impl.GetAttributesCommand; import io.toolisticon.aptk.tools.corematcher.AptkCoreMatchers; import io.toolisticon.aptk.tools.fluentfilter.FluentElementFilter; @@ -20,7 +19,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.regex.Pattern; /** * Utility class to handle bean related tasks. @@ -163,6 +161,7 @@ public static boolean isDefaultNoargConstructor(ExecutableElement element) { return false; } + /*- // now check statements of constructor List statements = ProcessingEnvironmentUtils.getTrees().getTree(element).getBody().getStatements(); @@ -171,7 +170,8 @@ public static boolean isDefaultNoargConstructor(ExecutableElement element) { } return Pattern.compile("^\\s*super\\(\\);?\\s*$").matcher(statements.get(0).toString()).matches(); - + */ + return true; } public static boolean isAttribute(VariableElement field) { diff --git a/tools/src/main/java/io/toolisticon/aptk/tools/ElementUtils.java b/tools/src/main/java/io/toolisticon/aptk/tools/ElementUtils.java index 3315e388..8c8c3319 100644 --- a/tools/src/main/java/io/toolisticon/aptk/tools/ElementUtils.java +++ b/tools/src/main/java/io/toolisticon/aptk/tools/ElementUtils.java @@ -42,6 +42,16 @@ public static final class CheckKindOfElement { */ private final static String KIND_MODULE = "MODULE"; + /** + * Needed for handling Record Type until source level is java 16. + */ + private final static String KIND_RECORD = "RECORD"; + + /** + * Needed for handling Record Component Type until source level is java 16. + */ + private final static String KIND_RECORD_COMPONENT = "RECORD_COMPONENT"; + /** * Hidden constructor. */ @@ -60,6 +70,26 @@ public static boolean isEnum(Element e) { return isOfKind(e, ElementKind.ENUM); } + /** + * Checks if passed Element instance is of kind enum. + * + * @param e the element to check + * @return true if passed element is of kind enum, otherwise false + */ + public static boolean isRecord(Element e) { + return (e != null && e.getKind().name().equals(KIND_RECORD)); + } + + /** + * Checks if passed Element instance is of kind enum. + * + * @param e the element to check + * @return true if passed element is of kind enum, otherwise false + */ + public static boolean isRecordComponent(Element e) { + return (e != null && e.getKind().name().equals(KIND_RECORD_COMPONENT)); + } + /** * Checks if passed Element instance is of kind class. * @@ -204,7 +234,8 @@ public static boolean isOfKind(Element e, ElementKind kind) { public static final class CastElement { // look up tables for the different kind of types - private static final Set TYPE_ELEMENT_KIND_LUT = Utilities.convertVarargsToSet(ElementKind.CLASS, ElementKind.INTERFACE, ElementKind.ENUM, ElementKind.ANNOTATION_TYPE); + private static final Set TYPE_ELEMENT_KIND_LUT = Utilities.convertVarargsToSet(ElementKind.CLASS.name(), ElementKind.INTERFACE.name(), ElementKind.ENUM.name(), ElementKind.ANNOTATION_TYPE.name(), "RECORD"); + private static final Set RECORD_COMPONENT_ELEMENT_KIND_LUT = Utilities.convertVarargsToSet( "RECORD_COMPONENT"); private static final Set TYPE_PARAMETER_ELEMENT_KIND_LUT = Utilities.convertVarargsToSet(ElementKind.TYPE_PARAMETER); private static final Set VARIABLE_ELEMENT_KIND_LUT = Utilities.convertVarargsToSet(ElementKind.PARAMETER, ElementKind.FIELD); private static final Set EXECUTABLE_ELEMENT_KIND_LUT = Utilities.convertVarargsToSet(ElementKind.CONSTRUCTOR, ElementKind.METHOD); @@ -227,7 +258,17 @@ private CastElement() { * @return true if passed element can be cast to TypeElement, otherwise false */ public static boolean isTypeElement(Element e) { - return e != null && TYPE_ELEMENT_KIND_LUT.contains(e.getKind()); + return e != null && TYPE_ELEMENT_KIND_LUT.contains(e.getKind().name()); + } + + /** + * Checks if passed element can be cast to RecordComponentElement. + * + * @param e the element to check + * @return true if passed element can be cast to RecordComponentElement, otherwise false + */ + public static boolean isRecordComponentElement(Element e) { + return e != null && RECORD_COMPONENT_ELEMENT_KIND_LUT.contains(e.getKind().name()); } /** diff --git a/tools/src/main/java/io/toolisticon/aptk/tools/ProcessingEnvironmentUtils.java b/tools/src/main/java/io/toolisticon/aptk/tools/ProcessingEnvironmentUtils.java index 1645872e..43799f44 100644 --- a/tools/src/main/java/io/toolisticon/aptk/tools/ProcessingEnvironmentUtils.java +++ b/tools/src/main/java/io/toolisticon/aptk/tools/ProcessingEnvironmentUtils.java @@ -1,6 +1,5 @@ package io.toolisticon.aptk.tools; -import com.sun.source.util.Trees; import io.toolisticon.aptk.common.ToolingProvider; import javax.annotation.processing.Filer; @@ -67,14 +66,4 @@ public static ProcessingEnvironment getProcessingEnvironment() { return ToolingProvider.getTooling().getProcessingEnvironment(); } - /** - * Gets Trees instance. - * - * @return The trees instance - */ - public static Trees getTrees() { - return Trees.instance(ToolingProvider.getTooling().getProcessingEnvironment()); - } - - } diff --git a/tools/src/main/java/io/toolisticon/aptk/tools/corematcher/AptkCoreMatchers.java b/tools/src/main/java/io/toolisticon/aptk/tools/corematcher/AptkCoreMatchers.java index 85c3511c..dc04e290 100644 --- a/tools/src/main/java/io/toolisticon/aptk/tools/corematcher/AptkCoreMatchers.java +++ b/tools/src/main/java/io/toolisticon/aptk/tools/corematcher/AptkCoreMatchers.java @@ -42,6 +42,7 @@ import io.toolisticon.aptk.tools.matcher.impl.IsPackageElementMatcher; import io.toolisticon.aptk.tools.matcher.impl.IsPackageMatcher; import io.toolisticon.aptk.tools.matcher.impl.IsParameterMatcher; +import io.toolisticon.aptk.tools.matcher.impl.IsRecordMatcher; import io.toolisticon.aptk.tools.matcher.impl.IsSetterMethodMatcher; import io.toolisticon.aptk.tools.matcher.impl.IsTypeElementMatcher; import io.toolisticon.aptk.tools.matcher.impl.IsTypeEqualFqnMatcher; @@ -252,67 +253,73 @@ protected AptkCoreMatchers() { /** * Matcher to check if passed element is a ExecutableElement. */ - public final static IsCoreMatcher IS_EXECUTABLE_ELEMENT = new IsCoreMatcher<>(new IsExecutableElementMatcher(), CoreMatcherValidationMessages.IS_EXECUTABLE_ELEMENT); + public final static IsCoreMatcher IS_EXECUTABLE_ELEMENT = new IsCoreMatcher<>(new IsExecutableElementMatcher<>(), CoreMatcherValidationMessages.IS_EXECUTABLE_ELEMENT); /** * Matcher to check if passed element is a TypeElement. */ - public final static IsCoreMatcher IS_TYPE_ELEMENT = new IsCoreMatcher<>(new IsTypeElementMatcher(), CoreMatcherValidationMessages.IS_TYPE_ELEMENT); + public final static IsCoreMatcher IS_TYPE_ELEMENT = new IsCoreMatcher<>(new IsTypeElementMatcher<>(), CoreMatcherValidationMessages.IS_TYPE_ELEMENT); /** * Matcher to check if passed element is a VariableElement. */ - public final static IsCoreMatcher IS_VARIABLE_ELEMENT = new IsCoreMatcher<>(new IsVariableElementMatcher(), CoreMatcherValidationMessages.IS_VARIABLE_ELEMENT); + public final static IsCoreMatcher IS_VARIABLE_ELEMENT = new IsCoreMatcher<>(new IsVariableElementMatcher<>(), CoreMatcherValidationMessages.IS_VARIABLE_ELEMENT); /** * Matcher to check if passed element is a PackageElement. */ - public final static IsCoreMatcher IS_PACKAGE_ELEMENT = new IsCoreMatcher<>(new IsPackageElementMatcher(), CoreMatcherValidationMessages.IS_PACKAGE_ELEMENT); + public final static IsCoreMatcher IS_PACKAGE_ELEMENT = new IsCoreMatcher<>(new IsPackageElementMatcher<>(), CoreMatcherValidationMessages.IS_PACKAGE_ELEMENT); /** * Matcher to check if passed element represents a package. */ - public final static IsElementBasedCoreMatcher IS_PACKAGE = new IsElementBasedCoreMatcher<>(new IsPackageMatcher(), CoreMatcherValidationMessages.IS_PACKAGE); + public final static IsElementBasedCoreMatcher IS_PACKAGE = new IsElementBasedCoreMatcher<>(new IsPackageMatcher<>(), CoreMatcherValidationMessages.IS_PACKAGE); /** * Matcher to check if passed element represents a class. */ - public final static IsElementBasedCoreMatcher IS_CLASS = new IsElementBasedCoreMatcher<>(new IsClassMatcher(), CoreMatcherValidationMessages.IS_CLASS); + public final static IsElementBasedCoreMatcher IS_CLASS = new IsElementBasedCoreMatcher<>(new IsClassMatcher<>(), CoreMatcherValidationMessages.IS_CLASS); /** * Matcher to check if passed element represents an enum. */ - public final static IsElementBasedCoreMatcher IS_ENUM = new IsElementBasedCoreMatcher<>(new IsEnumMatcher(), CoreMatcherValidationMessages.IS_ENUM); + public final static IsElementBasedCoreMatcher IS_ENUM = new IsElementBasedCoreMatcher<>(new IsEnumMatcher<>(), CoreMatcherValidationMessages.IS_ENUM); + + /** + * Matcher to check if passed element represents a record. + */ + public final static IsElementBasedCoreMatcher IS_RECORD = new IsElementBasedCoreMatcher<>(new IsRecordMatcher<>(), CoreMatcherValidationMessages.IS_RECORD); + /** * Matcher to check if passed element represents an interface. */ - public final static IsElementBasedCoreMatcher IS_INTERFACE = new IsElementBasedCoreMatcher<>(new IsInterfaceMatcher(), CoreMatcherValidationMessages.IS_INTERFACE); + public final static IsElementBasedCoreMatcher IS_INTERFACE = new IsElementBasedCoreMatcher<>(new IsInterfaceMatcher<>(), CoreMatcherValidationMessages.IS_INTERFACE); /** * Matcher to check if passed element represents a method. */ - public final static IsElementBasedCoreMatcher IS_METHOD = new IsElementBasedCoreMatcher<>(new IsMethodMatcher(), CoreMatcherValidationMessages.IS_METHOD); + public final static IsElementBasedCoreMatcher IS_METHOD = new IsElementBasedCoreMatcher<>(new IsMethodMatcher<>(), CoreMatcherValidationMessages.IS_METHOD); /** * Matcher to check if passed element represents a constructor. */ - public final static IsElementBasedCoreMatcher IS_CONSTRUCTOR = new IsElementBasedCoreMatcher<>(new IsConstructorMatcher(), CoreMatcherValidationMessages.IS_CONSTRUCTOR); + public final static IsElementBasedCoreMatcher IS_CONSTRUCTOR = new IsElementBasedCoreMatcher<>(new IsConstructorMatcher<>(), CoreMatcherValidationMessages.IS_CONSTRUCTOR); /** * Matcher to check if passed element represents an annotation type. */ - public final static IsElementBasedCoreMatcher IS_ANNOTATION_TYPE = new IsElementBasedCoreMatcher<>(new IsAnnotationTypeMatcher(), CoreMatcherValidationMessages.IS_ANNOTATION_TYPE); + public final static IsElementBasedCoreMatcher IS_ANNOTATION_TYPE = new IsElementBasedCoreMatcher<>(new IsAnnotationTypeMatcher<>(), CoreMatcherValidationMessages.IS_ANNOTATION_TYPE); /** * Matcher to check if passed element represents a field. */ - public final static IsElementBasedCoreMatcher IS_FIELD = new IsElementBasedCoreMatcher<>(new IsFieldMatcher(), CoreMatcherValidationMessages.IS_FIELD); + public final static IsElementBasedCoreMatcher IS_FIELD = new IsElementBasedCoreMatcher<>(new IsFieldMatcher<>(), CoreMatcherValidationMessages.IS_FIELD); /** * Matcher to check if passed element represents a parameter. */ - public final static IsElementBasedCoreMatcher IS_PARAMETER = new IsElementBasedCoreMatcher<>(new IsParameterMatcher(), CoreMatcherValidationMessages.IS_PARAMETER); + public final static IsElementBasedCoreMatcher IS_PARAMETER = new IsElementBasedCoreMatcher<>(new IsParameterMatcher<>(), CoreMatcherValidationMessages.IS_PARAMETER); } diff --git a/tools/src/main/java/io/toolisticon/aptk/tools/corematcher/CoreMatcherValidationMessages.java b/tools/src/main/java/io/toolisticon/aptk/tools/corematcher/CoreMatcherValidationMessages.java index aafe2a36..a27b3168 100644 --- a/tools/src/main/java/io/toolisticon/aptk/tools/corematcher/CoreMatcherValidationMessages.java +++ b/tools/src/main/java/io/toolisticon/aptk/tools/corematcher/CoreMatcherValidationMessages.java @@ -39,6 +39,9 @@ public enum CoreMatcherValidationMessages implements ValidationMessage { IS_CLASS("CM_IS_CLASS", "Element must ${0} represent a Class"), IS_ENUM("CM_IS_ENUM", "Element must ${0} represent an enum"), + + IS_RECORD("CM_IS_ENUM", "Element must ${0} represent a record"), + IS_INTERFACE("CM_IS_INTERFACE", "Element must ${0} represent an interface"), IS_METHOD("CM_IS_METHOD", "Element must ${0} represent a method"), diff --git a/tools/src/main/java/io/toolisticon/aptk/tools/matcher/impl/IsRecordMatcher.java b/tools/src/main/java/io/toolisticon/aptk/tools/matcher/impl/IsRecordMatcher.java new file mode 100644 index 00000000..ec3e479f --- /dev/null +++ b/tools/src/main/java/io/toolisticon/aptk/tools/matcher/impl/IsRecordMatcher.java @@ -0,0 +1,19 @@ +package io.toolisticon.aptk.tools.matcher.impl; + +import io.toolisticon.aptk.tools.ElementUtils; +import io.toolisticon.aptk.tools.matcher.ImplicitMatcher; + +import javax.lang.model.element.Element; + + +/** + * Implicit matcher that checks if a passed element is an enum. + */ +public class IsRecordMatcher implements ImplicitMatcher { + + @Override + public boolean check(ELEMENT element) { + return ElementUtils.CheckKindOfElement.isRecord(element); + } + +} \ No newline at end of file diff --git a/tools/src/main/java/io/toolisticon/aptk/tools/wrapper/ElementWrapper.java b/tools/src/main/java/io/toolisticon/aptk/tools/wrapper/ElementWrapper.java index 7f5075fb..99856592 100644 --- a/tools/src/main/java/io/toolisticon/aptk/tools/wrapper/ElementWrapper.java +++ b/tools/src/main/java/io/toolisticon/aptk/tools/wrapper/ElementWrapper.java @@ -16,6 +16,7 @@ import javax.lang.model.element.TypeElement; import java.lang.annotation.Annotation; import java.lang.annotation.Repeatable; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -93,6 +94,19 @@ public boolean hasSimplePackageName(String name) { return name != null && this.getSimplePackageName().equals(name); } + /** + * Gets the module of the wrapped element + * + * @return an optional containing the wrapped module element + */ + public Optional getModule() { + try { + Element element = ElementUtils.AccessEnclosingElements.getFirstEnclosingElementOfKind(this.element, ElementKind.valueOf("MODULE")); + return element != null ? Optional.of(ModuleElementWrapper.wrap(element)) : Optional.empty(); + } catch (IllegalArgumentException e) { + return Optional.empty(); + } + } /** * Gets the simple name of the element. @@ -175,6 +189,7 @@ public FluentElementFilter filterFlattenedEnclosedElementTree(boolean i /** * Gets all wrapped annotations of element. + * * @return a list containing all annotations of the element */ public List getAnnotations() { @@ -347,6 +362,7 @@ public Optional getAnnotationMirror(String annotationTy /** * Checks if passed annotation is present. + * * @param annotation the annotation tom check * @return true if passed annotation is present, otherwise false */ @@ -356,6 +372,7 @@ public boolean hasAnnotation(Class annotation) { /** * Checks if passed annotation is present. + * * @param annotationFqn the annotation tom check * @return true if passed annotation is present, otherwise false */ @@ -375,7 +392,7 @@ public Optional getAnnotation(Class annotationType) return Optional.ofNullable(element.getAnnotation(annotationType)); } - public Optional> getRepeatableAnnotation (Class annotationType) { + public Optional> getRepeatableAnnotation(Class annotationType) { List result = new ArrayList<>(); @@ -520,6 +537,24 @@ public boolean isEnum() { return ElementUtils.CheckKindOfElement.isEnum(this.element); } + /** + * Checks if wrapped element represents a record. + * + * @return true if wrapped element represents a record, otherwise false + */ + public boolean isRecord() { + return ElementUtils.CheckKindOfElement.isRecord(this.element); + } + + /** + * Checks if wrapped element represents a record. + * + * @return true if wrapped element represents a record, otherwise false + */ + public boolean isRecordComponent() { + return ElementUtils.CheckKindOfElement.isRecordComponent(this.element); + } + /** * Checks if wrapped element represents an annotation. * @@ -529,21 +564,24 @@ public boolean isAnnotation() { return ElementUtils.CheckKindOfElement.isAnnotation(this.element); } + /** * Checks if wrapped element is a repeatable annotation. + * * @return if wrapped element represents a repeatable annotation, otherwise false */ - public boolean isRepeatableAnnotation(){ + public boolean isRepeatableAnnotation() { return isAnnotation() && hasAnnotation(Repeatable.class); } /** * Gets an Optional containing the wrapped repeatable TypeMirror, if the wrapped represents a repeatable annotation. + * * @return The wrapped repeatable annotation Type Mirror or an empty Optional if it doesn't exist */ - public Optional getRepeatableWrapperType(){ + public Optional getRepeatableWrapperType() { - if(isRepeatableAnnotation()){ + if (isRepeatableAnnotation()) { return Optional.of(getAnnotationMirror(Repeatable.class).get().getAttribute().get().getClassValue()); } @@ -626,6 +664,14 @@ public boolean isTypeElement() { return ElementUtils.CastElement.isTypeElement(element); } + /** + * Checks if wrapped element is a RecordComponentElement. + * + * @return true if wrapped element is a RecordComponentElement, otherwise false + */ + public boolean isRecordComponentElement() { + return ElementUtils.CastElement.isRecordComponentElement(element); + } /** * Checks if wrapped element is a ExecutableElement. @@ -719,4 +765,33 @@ public static ExecutableElementWrapper toExecutableElementWrapper(ElementWrapper return ExecutableElementWrapper.wrap(ElementUtils.CastElement.castToExecutableElement(wrapper.unwrap())); } + + + protected TARGET_TYPE invokeParameterlessMethodOfElement(String interfaceName, String methodName) { + return ElementWrapper.invokeParameterlessMethodOfElement(element, interfaceName, methodName); + } + + protected static TARGET_TYPE invokeParameterlessMethodOfElement(Object instance, String interfaceName, String methodName) { + try { + Class interfaceClass = Class.forName(interfaceName); + return (TARGET_TYPE) interfaceClass.getMethod(methodName).invoke(instance); + } catch (Exception e) { + // This usually shouldn't be thrown since the caller must ensure that the call is available in the used java version + throw new IllegalStateException("Couldn't invoke " + interfaceName + "." + methodName + "()", e); + } + } + + protected boolean hasMethod(String interfaceName, String methodName) { + try { + + Class interfaceClass = Class.forName(interfaceName); + Method method = interfaceClass.getMethod(methodName); + return true; + + } catch (Exception e) { + // This usually shouldn't be thrown since the caller must ensure that the call is available in the used java version + return false; + } + } + } diff --git a/tools/src/main/java/io/toolisticon/aptk/tools/wrapper/ModuleElementWrapper.java b/tools/src/main/java/io/toolisticon/aptk/tools/wrapper/ModuleElementWrapper.java new file mode 100644 index 00000000..b1653979 --- /dev/null +++ b/tools/src/main/java/io/toolisticon/aptk/tools/wrapper/ModuleElementWrapper.java @@ -0,0 +1,378 @@ +package io.toolisticon.aptk.tools.wrapper; + +import javax.lang.model.element.Element; +import javax.lang.model.element.Name; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +public class ModuleElementWrapper extends ElementWrapper { + + private final static String MODULE_ELEMENT_CLASS_NAME = "javax.lang.model.element.ModuleElement"; + private final static String DIRECTIVE_CLASS_NAME = MODULE_ELEMENT_CLASS_NAME + "$Directive"; + private final static String EXPORTS_DIRECTIVE_CLASS_NAME = MODULE_ELEMENT_CLASS_NAME + "$ExportsDirective"; + + private final static String PROVIDES_DIRECTIVE_CLASS_NAME = MODULE_ELEMENT_CLASS_NAME + "$ProvidesDirective"; + + private final static String USES_DIRECTIVE_CLASS_NAME = MODULE_ELEMENT_CLASS_NAME + "$UsesDirective"; + + + private final static String OPENS_DIRECTIVE_CLASS_NAME = MODULE_ELEMENT_CLASS_NAME + "$OpensDirective"; + + private final static String REQUIRES_DIRECTIVE_CLASS_NAME = MODULE_ELEMENT_CLASS_NAME + "$RequiresDirective"; + + + public abstract static class Directive { + + protected final Object wrappedDirective; + + private Directive(Object wrappedDirective) { + this.wrappedDirective = wrappedDirective; + } + + public boolean isExportDirective() { + return isExportDirective(wrappedDirective); + } + + private static boolean isExportDirective(Object instance) { + return "EXPORTS".equals(ElementWrapper.invokeParameterlessMethodOfElement(instance, DIRECTIVE_CLASS_NAME, "getKind").toString()); + } + + public ExportsDirective toExportsDirective() { + return (ExportsDirective) this; + } + + + public boolean isOpensDirective() { + return isOpensDirective(wrappedDirective); + } + + private static boolean isOpensDirective(Object instance) { + return "OPENS".equals(ElementWrapper.invokeParameterlessMethodOfElement(instance, DIRECTIVE_CLASS_NAME, "getKind").toString()); + } + + public OpensDirective toOpensDirective() { + return (OpensDirective) this; + } + + public boolean isProvidesDirective() { + return isProvidesDirective(wrappedDirective); + } + + private static boolean isProvidesDirective(Object instance) { + return "PROVIDES".equals(ElementWrapper.invokeParameterlessMethodOfElement(instance, DIRECTIVE_CLASS_NAME, "getKind").toString()); + } + + public ProvidesDirective toProvidesDirective() { + return (ProvidesDirective) this; + } + + public boolean isRequiresDirective() { + return isRequiresDirective(wrappedDirective); + } + + private static boolean isRequiresDirective(Object instance) { + return "REQUIRES".equals(ElementWrapper.invokeParameterlessMethodOfElement(instance, DIRECTIVE_CLASS_NAME, "getKind").toString()); + } + + public RequiresDirective toRequiresDirective() { + return (RequiresDirective) this; + } + + public boolean isUsesDirective() { + return isUsesDirective(wrappedDirective); + } + + private static boolean isUsesDirective(Object instance) { + return "USES".equals(ElementWrapper.invokeParameterlessMethodOfElement(instance, DIRECTIVE_CLASS_NAME, "getKind").toString()); + } + + + public UsesDirective toUsesDirective() { + return (UsesDirective) this; + } + + + } + + + public interface ExportsDirective { + + /** + * Returns the package being exported. + * + * @return the package being exported + */ + PackageElementWrapper getPackage(); + + /** + * Returns the specific modules to which the package is being exported, or null, if the package is exported to all modules which have readability to this module. + * + * @return the specific modules to which the package is being exported + */ + List getTargetModules(); + + } + + static class ExportsDirectiveImpl extends Directive implements ExportsDirective { + + + private ExportsDirectiveImpl(Object wrappedDirective) { + super(wrappedDirective); + } + + @Override + public PackageElementWrapper getPackage() { + return PackageElementWrapper.wrap( ElementWrapper.invokeParameterlessMethodOfElement(wrappedDirective, EXPORTS_DIRECTIVE_CLASS_NAME, "getPackage")); + } + + @Override + public List getTargetModules() { + return ModuleElementWrapper.wrapList(ElementWrapper.>invokeParameterlessMethodOfElement(wrappedDirective, EXPORTS_DIRECTIVE_CLASS_NAME, "getTargetModules")); + } + + + } + + public interface OpensDirective { + + + /** + * Returns the package being opened. + * + * @return the package being opened + */ + PackageElementWrapper getPackage(); + + /** + * Returns the specific modules to which the package is being open or null, if the package is open all modules which have readability to this module. + * + * @return the specific modules to which the package is being opened + */ + List getTargetModules(); + + } + + static class OpensDirectiveImpl extends Directive implements OpensDirective { + + + private OpensDirectiveImpl(Object wrappedDirective) { + super(wrappedDirective); + } + + @Override + public PackageElementWrapper getPackage() { + return PackageElementWrapper.wrap(ElementWrapper.invokeParameterlessMethodOfElement(wrappedDirective, OPENS_DIRECTIVE_CLASS_NAME, "getPackage")); + } + + @Override + public List getTargetModules() { + return ModuleElementWrapper.wrapList(ElementWrapper.>invokeParameterlessMethodOfElement(wrappedDirective, OPENS_DIRECTIVE_CLASS_NAME, "getTargetModules")); + } + + } + + public interface ProvidesDirective { + + /** + * Returns the service being provided + * + * @return the service being provided + */ + TypeElementWrapper getService(); + + /** + * Returns the implementations of the service being provided. + * + * @return the implementations of the service being provided + */ + List getImplementations(); + + } + + static class ProvidesDirectiveImpl extends Directive implements ProvidesDirective { + + + private ProvidesDirectiveImpl(Object wrappedDirective) { + super(wrappedDirective); + } + + @Override + public TypeElementWrapper getService() { + return TypeElementWrapper.wrap(ElementWrapper.invokeParameterlessMethodOfElement(wrappedDirective, PROVIDES_DIRECTIVE_CLASS_NAME, "getService")); + } + + @Override + public List getImplementations() { + List implementations = (ElementWrapper.>invokeParameterlessMethodOfElement(wrappedDirective, PROVIDES_DIRECTIVE_CLASS_NAME, "getImplementations")); + return implementations.stream().map(TypeElementWrapper::wrap).collect(Collectors.toList()); + } + + } + + public interface RequiresDirective { + + /** + * Returns the module that is required + * + * @return the module that is required + */ + ModuleElementWrapper getDependency(); + + + /** + * Returns whether or not this is a static dependency. + * + * @return whether or not this is a static dependency. + */ + boolean isStatic(); + + /** + * Returns whether or not this is a transitive dependency. + * + * @return whether or not this is a transitive dependency. + */ + boolean isTransitive(); + + } + + static class RequiresDirectiveImpl extends Directive implements RequiresDirective { + + + private RequiresDirectiveImpl(Object wrappedDirective) { + super(wrappedDirective); + } + + @Override + public ModuleElementWrapper getDependency() { + return ModuleElementWrapper.wrap(ElementWrapper.invokeParameterlessMethodOfElement(wrappedDirective, REQUIRES_DIRECTIVE_CLASS_NAME, "getDependency")); + } + + + @Override + public boolean isStatic() { + return ElementWrapper.invokeParameterlessMethodOfElement(wrappedDirective, REQUIRES_DIRECTIVE_CLASS_NAME, "isStatic"); + } + + @Override + public boolean isTransitive() { + return ElementWrapper.invokeParameterlessMethodOfElement(wrappedDirective, REQUIRES_DIRECTIVE_CLASS_NAME, "isTransitive"); + } + + } + + public interface UsesDirective { + + /** + * Returns the service being used + * + * @return the service being used + */ + TypeElementWrapper getService(); + + + } + + public static class UsesDirectiveImpl extends Directive implements UsesDirective { + + private UsesDirectiveImpl(Object wrappedDirective) { + super(wrappedDirective); + } + + @Override + public TypeElementWrapper getService() { + return TypeElementWrapper.wrap(ElementWrapper.invokeParameterlessMethodOfElement(wrappedDirective, USES_DIRECTIVE_CLASS_NAME, "getService")); + } + + + } + + private static Directive createDirective(Object instance) { + if (instance == null) { + return null; + } + + if (Directive.isExportDirective(instance)) { + return new ExportsDirectiveImpl(instance); + } else if (Directive.isOpensDirective(instance)) { + return new OpensDirectiveImpl(instance); + } else if (Directive.isProvidesDirective(instance)) { + return new ProvidesDirectiveImpl(instance); + } else if (Directive.isRequiresDirective(instance)) { + return new RequiresDirectiveImpl(instance); + } else if (Directive.isUsesDirective(instance)) { + return new UsesDirectiveImpl(instance); + } + + return null; + } + + private static List createDirectives(List directives) { + + return directives.stream().map(e -> createDirective(e)).filter(Objects::nonNull).collect(Collectors.toList()); + + } + + private ModuleElementWrapper(Element moduleElement) { + super(moduleElement); + } + + public String getQualifiedName() { + return this.invokeParameterlessMethodOfElement(MODULE_ELEMENT_CLASS_NAME, "getQualifiedName").toString(); + } + + public boolean hasQualifiedName(String name) { + return name != null && this.getQualifiedName().equals(name); + } + + public boolean isOpen() { + return this.invokeParameterlessMethodOfElement(MODULE_ELEMENT_CLASS_NAME, "isOpen"); + } + + public boolean isUnnamed() { + return this.invokeParameterlessMethodOfElement(MODULE_ELEMENT_CLASS_NAME, "isUnnamed"); + } + + + public List getDirectives() { + return createDirectives(this.>invokeParameterlessMethodOfElement(MODULE_ELEMENT_CLASS_NAME, "getDirectives")); + } + + + /** + * Converts an ElementWrapper to a ModuleElementWrapper id the wrapped element is a ModuleElement + * + * @param element the element to convert + * @return a ModuleElementWrapper wrapping passed ElementWrapper wraps a ModuleElement, otherwise null + */ + public static ModuleElementWrapper toModuleElement(ElementWrapper element) { + return ModuleElementWrapper.wrap(element.unwrap()); + } + + /** + * Wraps the passed element in ModuleElementWrapper if it is a ModuleElement. + * + * @param element the element to wrap + * @return a ModuleElementWrapper wrapping passed element if it is a ModuleElement, otherwise null + */ + public static ModuleElementWrapper wrap(Element element) { + return (element == null || !"MODULE".equals(element.getKind().name())) ? null : new ModuleElementWrapper(element); + } + + /** + * Wraps a List of ModuleElements. + * Unfortunately because of backward compatibility it is not possible to limit lists component type to ModuleElement. + * So this method will try to wrap all Elements of the passed list. It will drop those not being ModuleElements. + * + * @param moduleElements A List of ModuleElements. + * @return A list of ModuleElementWrapper of elements representing modules that could successfully be wrapped, or an empty list. + */ + public static List wrapList(List moduleElements) { + return moduleElements.stream().map(ModuleElementWrapper::wrap).filter(Objects::nonNull).collect(Collectors.toList()); + } + +} diff --git a/tools/src/main/java/io/toolisticon/aptk/tools/wrapper/RecordComponentElementWrapper.java b/tools/src/main/java/io/toolisticon/aptk/tools/wrapper/RecordComponentElementWrapper.java new file mode 100644 index 00000000..6643952a --- /dev/null +++ b/tools/src/main/java/io/toolisticon/aptk/tools/wrapper/RecordComponentElementWrapper.java @@ -0,0 +1,81 @@ +package io.toolisticon.aptk.tools.wrapper; + +import io.toolisticon.aptk.tools.corematcher.AptkCoreMatchers; + +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; +import java.util.List; + +/** + * Wrapper class for RecordComponentElementWrapper. + */ +public class RecordComponentElementWrapper extends ElementWrapper { + + private final static String RECORD_COMPONENT_ELEMENT_CLASS_NAME = "javax.lang.model.element.RecordComponentElement"; + + + private RecordComponentElementWrapper(Element recordComponentElement) { + super(recordComponentElement); + } + + /** + * Gets the enclosing records TypeElement + * + * @return the wrapped enclosing records TypeElement + */ + public TypeElementWrapper getEnclosingRecordTypeElement() { + return TypeElementWrapper.wrap((TypeElement) element.getEnclosingElement()); + } + + /** + * Wraps the getAccessor method, but returns a ExecutableElementWrapper + * !!! WARNING THERE SEEMS TO BE DIFFERENT BEHAVIOR BETWEEN DIFFERENT JDK DISTRIBUTIONS !!! + * So it will look up the accessor manually if necessary + * @return the accessors wrapped ExecutableElement, might return null if even the workaround doesn't work + */ + public ExecutableElementWrapper getAccessor() { + // safe to call since it's guaranteed that the wrapped element is a RecordComponentElement + ExecutableElement executableElement = this.invokeParameterlessMethodOfElement(RECORD_COMPONENT_ELEMENT_CLASS_NAME, "getAccessor"); + + return executableElement != null ? ExecutableElementWrapper.wrap(executableElement) : determineAccessorWorkaround(); + } + + private ExecutableElementWrapper determineAccessorWorkaround(){ + List results = this.getEnclosingRecordTypeElement().filterEnclosedElements() + .applyFilter(AptkCoreMatchers.IS_METHOD) + .applyFilter(AptkCoreMatchers.BY_NAME).filterByOneOf(getSimpleName()) + .applyFilter(AptkCoreMatchers.HAS_NO_PARAMETERS) + .getResult(); + + return results.isEmpty() ? null : ExecutableElementWrapper.wrap(results.get(0)); + } + + /** + * Re-wraps an ElementWrapper to a RecordComponentElementWrapper. + * + * @param element the wrapper to re-wrap + * @return The RecordComponentElementWrapper or null if the passed ElementWrapper doesn't wrap a RecordComponentElement + */ + public static RecordComponentElementWrapper toRecordComponentElement(ElementWrapper element) { + if (element == null) { + return null; + } + return RecordComponentElementWrapper.wrap(element.unwrap()); + } + + /** + * Wraps an element with the RecordComponentElementWrapper. + * + * @param element the element to wrap + * @return the wrapped element, or null if passed element isn't an RecordComponentElement + */ + public static RecordComponentElementWrapper wrap(Element element) { + if (element == null || !"RECORD_COMPONENT".equals(element.getKind().name())) { + return null; + } + return new RecordComponentElementWrapper(element); + } + + +} diff --git a/tools/src/main/java/io/toolisticon/aptk/tools/wrapper/TypeElementWrapper.java b/tools/src/main/java/io/toolisticon/aptk/tools/wrapper/TypeElementWrapper.java index 830676e0..dffc6504 100644 --- a/tools/src/main/java/io/toolisticon/aptk/tools/wrapper/TypeElementWrapper.java +++ b/tools/src/main/java/io/toolisticon/aptk/tools/wrapper/TypeElementWrapper.java @@ -5,6 +5,7 @@ import io.toolisticon.aptk.tools.corematcher.AptkCoreMatchers; import io.toolisticon.aptk.tools.fluentfilter.FluentElementFilter; +import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; @@ -12,6 +13,7 @@ import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeMirror; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Optional; @@ -23,6 +25,8 @@ */ public class TypeElementWrapper extends ElementWrapper { + private final static String TYPE_ELEMENT_CLASS_NAME = "javax.lang.model.element.TypeElement"; + /** * Hidden constructor. * @@ -261,6 +265,39 @@ public List getEnumValues() { : null; } + /** + * Returns the record components of this class or interface element in declaration order. + * @return the record components, or an empty list if there are none + */ + public List getRecordComponents() { + + // This method is available from Java 16 - the introduction of records, so this check is sufficient to prevent reflective calling of method + if (!isRecord()) { + return Collections.EMPTY_LIST; + } + List recordComponentElements = this.>invokeParameterlessMethodOfElement(TYPE_ELEMENT_CLASS_NAME, "getRecordComponents"); + + return recordComponentElements.stream().map(RecordComponentElementWrapper::wrap).collect(Collectors.toList()); + + } + + /** + * Returns the permitted classes of this class or interface element in declaration order. + * @return the permitted classes, or an empty list if there are none + */ + public List getPermittedSubclasses() { + + // must make sure that method exists, otherwise return the default value + if (!hasMethod(TYPE_ELEMENT_CLASS_NAME, "getPermittedSubclasses")) { + // TODO MUST CHECK WHAT SHOULD BE RETURNED FOR NON SEALED CLASSES! + return Collections.EMPTY_LIST; + } + + List typeMirrors = this.>invokeParameterlessMethodOfElement(TYPE_ELEMENT_CLASS_NAME, "getPermittedSubclasses"); + return typeMirrors.stream().map(TypeMirrorWrapper::wrap).collect(Collectors.toList()); + } + + /** * Wraps a TypeElement. * diff --git a/tools/src/test/java/io/toolisticon/aptk/tools/BeanUtilsTest.java b/tools/src/test/java/io/toolisticon/aptk/tools/BeanUtilsTest.java index e5300485..688d80b8 100644 --- a/tools/src/test/java/io/toolisticon/aptk/tools/BeanUtilsTest.java +++ b/tools/src/test/java/io/toolisticon/aptk/tools/BeanUtilsTest.java @@ -1,6 +1,5 @@ package io.toolisticon.aptk.tools; -import com.sun.source.tree.StatementTree; import io.toolisticon.aptk.cute.APTKUnitTestProcessor; import io.toolisticon.aptk.tools.corematcher.AptkCoreMatchers; import io.toolisticon.aptk.tools.fluentfilter.FluentElementFilter; @@ -10,6 +9,7 @@ import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import javax.annotation.processing.ProcessingEnvironment; @@ -18,7 +18,6 @@ import javax.lang.model.element.VariableElement; import java.util.Arrays; import java.util.HashSet; -import java.util.List; import java.util.Set; /** @@ -322,6 +321,7 @@ public void aptkUnitTest(ProcessingEnvironment processingEnvironment, TypeElemen } @Test + @Ignore public void isDefaultNoargConstructor_explicitNoargConstructor() { unitTestBuilder @@ -359,9 +359,6 @@ public void aptkUnitTest(ProcessingEnvironment processingEnvironment, TypeElemen .applyFilter(AptkCoreMatchers.IS_CONSTRUCTOR) .getResult().get(0); - List statements = ProcessingEnvironmentUtils.getTrees().getTree(constructor).getBody().getStatements(); - - MatcherAssert.assertThat("Should return true for explicit constructor that looks like default constructor", BeanUtils.isDefaultNoargConstructor(constructor)); MatcherAssert.assertThat("Should return true for explicit constructor that looks like default constructor", BeanUtils.hasDefaultNoargsConstructor(element)); diff --git a/tools/src/test/java/io/toolisticon/aptk/tools/wrapper/ModulesElementWrapperTest.java b/tools/src/test/java/io/toolisticon/aptk/tools/wrapper/ModulesElementWrapperTest.java new file mode 100644 index 00000000..63672ce4 --- /dev/null +++ b/tools/src/test/java/io/toolisticon/aptk/tools/wrapper/ModulesElementWrapperTest.java @@ -0,0 +1,60 @@ +package io.toolisticon.aptk.tools.wrapper; + +import io.toolisticon.aptk.common.ToolingProvider; +import io.toolisticon.cute.Cute; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.Test; + +import javax.lang.model.element.Element; +import javax.tools.Tool; +import java.util.Arrays; +import java.util.Optional; + +/** + * Unit test for {@link ModuleElementWrapper}. + * Only handles wrapping part since this module is build with Java version <9. + */ +public class ModulesElementWrapperTest { + + @Test + public void test_invalidWrap () { + Cute.unitTest().when().unitTestWithoutPassIn(processingEnvironment -> { + try{ + ToolingProvider.setTooling(processingEnvironment); + + MatcherAssert.assertThat(ModuleElementWrapper.wrap((Element) TypeElementWrapper.getByClass(ModulesElementWrapperTest.class).get().unwrap()),Matchers.nullValue()); + } finally { + ToolingProvider.clearTooling(); + } + }).executeTest(); + } + + @Test + public void test_invalidToModuleElement () { + Cute.unitTest().when().unitTestWithoutPassIn(processingEnvironment -> { + try{ + ToolingProvider.setTooling(processingEnvironment); + + MatcherAssert.assertThat(ModuleElementWrapper.toModuleElement((ElementWrapper) TypeElementWrapper.getByClass(ModulesElementWrapperTest.class).get()),Matchers.nullValue()); + } finally { + ToolingProvider.clearTooling(); + } + }).executeTest(); + } + + @Test + public void test_invalidWrapOfList () { + Cute.unitTest().when().unitTestWithoutPassIn(processingEnvironment -> { + try{ + ToolingProvider.setTooling(processingEnvironment); + + MatcherAssert.assertThat(ModuleElementWrapper.wrapList(Arrays.asList( TypeElementWrapper.getByClass(ModulesElementWrapperTest.class).get().unwrap())),Matchers.empty()); + } finally { + ToolingProvider.clearTooling(); + } + }).executeTest(); + } + + +}