Skip to content

Commit f9b2063

Browse files
authored
Amazon Q Feature - Use language level from project and module settings (#4545)
* Feature - add language level JDK support to validation checks for project structure and modules
1 parent 6759455 commit f9b2063

File tree

4 files changed

+196
-24
lines changed

4 files changed

+196
-24
lines changed

plugins/amazonq/codetransform/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codemodernizer/utils/CodeTransformModuleUtils.kt

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,33 @@ import com.intellij.openapi.module.Module
77
import com.intellij.openapi.project.Project
88
import com.intellij.openapi.projectRoots.JavaSdkVersion
99
import com.intellij.openapi.projectRoots.impl.JavaSdkImpl
10+
import com.intellij.openapi.roots.LanguageLevelModuleExtensionImpl
1011
import com.intellij.openapi.roots.ModuleRootManager
1112
import com.intellij.openapi.roots.ProjectRootManager
1213

1314
/**
14-
* @description Try to get the module SDK version and fallback to the project SDK version from the "project structure" settings.
15+
* @description Try to get the module SDK version and/or Language level from the project settings > modules > source field.
16+
* If module inherits from defaults, fallback to the project SDK version from the "project structure" settings.
1517
*/
1618
fun Module.tryGetJdk(project: Project): JavaSdkVersion? {
17-
val sdk = ModuleRootManager.getInstance(this).sdk ?: ProjectRootManager.getInstance(project).projectSdk ?: return null
1819
val javaSdk = JavaSdkImpl.getInstance()
19-
return javaSdk.getVersion(sdk)
20+
val moduleRootManager = ModuleRootManager.getInstance(this)
21+
val moduleLanguageLevel = this.tryGetJdkLanguageLevelJdk() ?: moduleRootManager.sdk?.let { javaSdk.getVersion(it) }
22+
val projectSdk = ProjectRootManager.getInstance(project).projectSdk
23+
val projectSdkVersion = projectSdk?.let { javaSdk.getVersion(it) }
24+
return moduleLanguageLevel ?: projectSdkVersion
25+
}
26+
27+
/**
28+
* @description Try to get the project SDK "language level" version from the module "source" settings.
29+
* The default value should be set to the project settings SDK and language level, so if the parent SDK is set to
30+
* Java 17 and the language level is set to default. The value spit out will be JDK_17. If the parent language
31+
* level is set to JDK_1_8 then the default will be JDK_1_8 for the module. You can override all this at the
32+
* module "source" setting
33+
*/
34+
fun Module.tryGetJdkLanguageLevelJdk(): JavaSdkVersion? {
35+
val moduleRootManager = ModuleRootManager.getInstance(this)
36+
val languageLevelModuleExtension = moduleRootManager.getModuleExtension(LanguageLevelModuleExtensionImpl::class.java)
37+
val languageLevel = languageLevelModuleExtension?.languageLevel
38+
return languageLevel?.let { JavaSdkVersion.fromLanguageLevel(it) } ?: null
2039
}

plugins/amazonq/codetransform/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codemodernizer/utils/CodeTransformProjectUtils.kt

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import com.intellij.openapi.project.Project
99
import com.intellij.openapi.project.modules
1010
import com.intellij.openapi.projectRoots.JavaSdkVersion
1111
import com.intellij.openapi.projectRoots.impl.JavaSdkImpl
12+
import com.intellij.openapi.roots.LanguageLevelProjectExtension
1213
import com.intellij.openapi.roots.ProjectRootManager
1314
import com.intellij.openapi.vfs.VirtualFile
1415

@@ -17,20 +18,28 @@ import com.intellij.openapi.vfs.VirtualFile
1718
*/
1819
fun Project.tryGetJdk(): JavaSdkVersion? {
1920
val projectSdk = ProjectRootManager.getInstance(this).projectSdk
21+
val languagelevelSdk = this.tryGetJdkLanguageLevelJdk()
2022
val javaSdk = JavaSdkImpl.getInstance()
21-
return javaSdk.getVersion(projectSdk ?: return null)
23+
if (languagelevelSdk != null) {
24+
return languagelevelSdk
25+
}
26+
return projectSdk?.let { javaSdk.getVersion(it) }
2227
}
2328

24-
fun Project.getSupportedJavaMappings(supportedJavaMappings: Map<JavaSdkVersion, Set<JavaSdkVersion>>): List<String> {
25-
val projectSdk = ProjectRootManager.getInstance(this).projectSdk
26-
val javaSdk = JavaSdkImpl.getInstance()
27-
return if (projectSdk == null) {
28-
listOf()
29-
} else {
30-
supportedJavaMappings.getOrDefault(javaSdk.getVersion(projectSdk), listOf()).map { it.name }.toList()
31-
}
29+
/**
30+
* @description Try to get the project SDK "language level" version from the "project structure" settings.
31+
* The default value should be set to the SDK, so if the parent SDK is set to Java 17 and the language level
32+
* is set to default. The value spit out will be JDK_17.
33+
*/
34+
fun Project.tryGetJdkLanguageLevelJdk(): JavaSdkVersion? {
35+
val languageLevelExtension = LanguageLevelProjectExtension.getInstance(this)
36+
val languageLevel = languageLevelExtension?.languageLevel
37+
return languageLevel?.let { JavaSdkVersion.fromLanguageLevel(it) }
3238
}
3339

40+
fun Project.getSupportedJavaMappings(supportedJavaMappings: Map<JavaSdkVersion, Set<JavaSdkVersion>>): List<String> =
41+
supportedJavaMappings.getOrDefault(this.tryGetJdk(), listOf()).map { it.name }.toList()
42+
3443
private fun Project.getAllSupportedBuildFiles(supportedBuildFileNames: List<String>): List<VirtualFile> {
3544
/**
3645
* Strategy:
@@ -64,18 +73,6 @@ fun Project.getSupportedBuildFilesWithSupportedJdk(
6473
}
6574
}
6675

67-
fun Project.getSupportedBuildModulesPath(supportedBuildFileNames: List<String>): List<String> {
68-
val projectRootManager = ProjectRootManager.getInstance(this)
69-
val probableProjectRoot = this.basePath?.toVirtualFile() // May point to only one intellij module (the first opened one)
70-
val probableContentRoots = projectRootManager.contentRoots.toMutableSet() // May not point to the topmost folder of modules
71-
probableContentRoots.add(probableProjectRoot) // dedupe
72-
val topLevelRoots = filterOnlyParentFiles(probableContentRoots)
73-
val detectedBuildFilePaths = topLevelRoots.flatMap { root ->
74-
findBuildFiles(root.toNioPath().toFile(), supportedBuildFileNames).mapNotNull { it.path }
75-
}
76-
return detectedBuildFilePaths
77-
}
78-
7976
fun Project.getSupportedModules(supportedJavaMappings: Map<JavaSdkVersion, Set<JavaSdkVersion>>) = this.modules.filter {
8077
val moduleJdk = it.tryGetJdk(this) ?: return@filter false
8178
moduleJdk in supportedJavaMappings
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package software.aws.toolkits.jetbrains.services.codemodernizer.utils
5+
6+
import com.intellij.openapi.projectRoots.JavaSdk
7+
import com.intellij.openapi.projectRoots.JavaSdkVersion
8+
import com.intellij.openapi.projectRoots.Sdk
9+
import com.intellij.openapi.projectRoots.impl.JavaSdkImpl
10+
import com.intellij.openapi.roots.LanguageLevelModuleExtensionImpl
11+
import com.intellij.openapi.roots.ModuleRootManager
12+
import com.intellij.openapi.roots.ProjectRootManager
13+
import com.intellij.pom.java.LanguageLevel
14+
import org.junit.Assert.assertEquals
15+
import org.junit.Before
16+
import org.junit.Test
17+
import org.mockito.Mockito.mockStatic
18+
import org.mockito.kotlin.any
19+
import org.mockito.kotlin.doReturn
20+
import org.mockito.kotlin.mock
21+
import org.mockito.kotlin.spy
22+
import org.mockito.kotlin.whenever
23+
import software.aws.toolkits.jetbrains.services.codemodernizer.CodeWhispererCodeModernizerTestBase
24+
import software.aws.toolkits.jetbrains.utils.rules.HeavyJavaCodeInsightTestFixtureRule
25+
26+
class CodeTransformModuleUtilsTest : CodeWhispererCodeModernizerTestBase(HeavyJavaCodeInsightTestFixtureRule()) {
27+
lateinit var javaSdkMock: JavaSdk
28+
lateinit var sdkMock: Sdk
29+
lateinit var moduleRootManagerMock: ModuleRootManager
30+
lateinit var projectRootManagerMock: ProjectRootManager
31+
lateinit var languageLevelModuleExtensionMock: LanguageLevelModuleExtensionImpl
32+
33+
@Before
34+
override fun setup() {
35+
super.setup()
36+
mockStatic(ModuleRootManager::class.java)
37+
mockStatic(ProjectRootManager::class.java)
38+
mockStatic(JavaSdkImpl::class.java)
39+
40+
javaSdkMock = mock<JavaSdkImpl>()
41+
sdkMock = spy<Sdk>()
42+
moduleRootManagerMock = mock<ModuleRootManager>()
43+
projectRootManagerMock = mock<ProjectRootManager>()
44+
languageLevelModuleExtensionMock = mock<LanguageLevelModuleExtensionImpl>()
45+
46+
whenever(ModuleRootManager.getInstance(module)).doReturn(moduleRootManagerMock)
47+
whenever(ProjectRootManager.getInstance(project)).doReturn(projectRootManagerMock)
48+
whenever(moduleRootManagerMock.getModuleExtension(LanguageLevelModuleExtensionImpl::class.java)).doReturn(languageLevelModuleExtensionMock)
49+
}
50+
51+
@Test
52+
fun `CodeTransformModuleUtils tryGetJdk() function returns module language level when set`() {
53+
whenever(moduleRootManagerMock.sdk).doReturn(sdkMock)
54+
whenever(projectRootManagerMock.projectSdk).doReturn(null)
55+
whenever(languageLevelModuleExtensionMock.languageLevel).doReturn(LanguageLevel.JDK_1_8)
56+
whenever(javaSdkMock.getVersion(any())).doReturn(JavaSdkVersion.JDK_1_8)
57+
val result = module.tryGetJdk(project)
58+
assertEquals(JavaSdkVersion.JDK_1_8, result)
59+
}
60+
61+
@Test
62+
fun `CodeTransformModuleUtils tryGetJdk() function returns null when project and module sdk and language level is not set`() {
63+
whenever(languageLevelModuleExtensionMock.languageLevel).doReturn(null)
64+
whenever(javaSdkMock.getVersion(sdkMock)).doReturn(null)
65+
val result = module.tryGetJdk(project)
66+
assertEquals(null, result)
67+
}
68+
69+
@Test
70+
fun `CodeTransformModuleUtils tryGetJdkLanguageLevelJdk() function returns null when language level is null`() {
71+
whenever(languageLevelModuleExtensionMock.languageLevel).doReturn(null)
72+
whenever(javaSdkMock.getVersion(any())).doReturn(JavaSdkVersion.JDK_17)
73+
val result = module.tryGetJdkLanguageLevelJdk()
74+
assertEquals(null, result)
75+
}
76+
77+
@Test
78+
fun `CodeTransformModuleUtils tryGetJdkLanguageLevelJdk() function returns language level version`() {
79+
whenever(languageLevelModuleExtensionMock.languageLevel).doReturn(LanguageLevel.JDK_1_8)
80+
whenever(javaSdkMock.getVersion(any())).doReturn(JavaSdkVersion.JDK_1_8)
81+
val result = module.tryGetJdkLanguageLevelJdk()
82+
assertEquals(JavaSdkVersion.JDK_1_8, result)
83+
}
84+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package software.aws.toolkits.jetbrains.services.codemodernizer.utils
5+
6+
import com.intellij.openapi.projectRoots.JavaSdkVersion
7+
import com.intellij.openapi.projectRoots.Sdk
8+
import com.intellij.openapi.roots.LanguageLevelProjectExtension
9+
import com.intellij.openapi.roots.ProjectRootManager
10+
import com.intellij.pom.java.LanguageLevel
11+
import com.intellij.testFramework.IdeaTestUtil.getMockJdk21
12+
import org.junit.Assert.assertEquals
13+
import org.junit.Before
14+
import org.mockito.Mockito.mockStatic
15+
import org.mockito.kotlin.doReturn
16+
import org.mockito.kotlin.mock
17+
import org.mockito.kotlin.whenever
18+
import software.aws.toolkits.jetbrains.services.codemodernizer.CodeWhispererCodeModernizerTestBase
19+
import kotlin.test.Test
20+
21+
class CodeTransformProjectUtilsTest : CodeWhispererCodeModernizerTestBase() {
22+
lateinit var projectRootManagerMock: ProjectRootManager
23+
lateinit var languageLevelProjectExtensionMock: LanguageLevelProjectExtension
24+
lateinit var sdkMock: Sdk
25+
26+
@Before
27+
override fun setup() {
28+
super.setup()
29+
mockStatic(LanguageLevelProjectExtension::class.java)
30+
31+
sdkMock = getMockJdk21()
32+
languageLevelProjectExtensionMock = mock<LanguageLevelProjectExtension>()
33+
34+
whenever(LanguageLevelProjectExtension.getInstance(project)).doReturn(languageLevelProjectExtensionMock)
35+
}
36+
37+
@Test
38+
fun `CodeTransformProjectUtils tryGetJdk() function returns project SDK when module language level is not set`() {
39+
mockStatic(ProjectRootManager::class.java)
40+
projectRootManagerMock = mock<ProjectRootManager>()
41+
whenever(ProjectRootManager.getInstance(project)).doReturn((projectRootManagerMock))
42+
whenever(projectRootManagerMock.projectSdk).doReturn(sdkMock)
43+
whenever(LanguageLevelProjectExtension.getInstance(project)).doReturn(null)
44+
val result = project.tryGetJdk()
45+
assertEquals(JavaSdkVersion.JDK_21, result)
46+
}
47+
48+
@Test
49+
fun `CodeTransformProjectUtils tryGetJdk() function returns project SDK when module language level is set`() {
50+
mockStatic(ProjectRootManager::class.java)
51+
projectRootManagerMock = mock<ProjectRootManager>()
52+
whenever(ProjectRootManager.getInstance(project)).doReturn(projectRootManagerMock)
53+
whenever(projectRootManagerMock.projectSdk).doReturn(sdkMock)
54+
whenever(languageLevelProjectExtensionMock.languageLevel).doReturn(LanguageLevel.JDK_1_8)
55+
val result = project.tryGetJdk()
56+
assertEquals(JavaSdkVersion.JDK_1_8, result)
57+
}
58+
59+
@Test
60+
fun `CodeTransformProjectUtils tryGetJdkLanguageLevelJdk() function returns null when language level is null`() {
61+
whenever(LanguageLevelProjectExtension.getInstance(project)).doReturn(null)
62+
val result = project.tryGetJdkLanguageLevelJdk()
63+
assertEquals(null, result)
64+
}
65+
66+
@Test
67+
fun `CodeTransformProjectUtils tryGetJdkLanguageLevelJdk() function returns language level version`() {
68+
whenever(languageLevelProjectExtensionMock.languageLevel).doReturn(LanguageLevel.JDK_1_8)
69+
val result = project.tryGetJdkLanguageLevelJdk()
70+
assertEquals(JavaSdkVersion.JDK_1_8, result)
71+
}
72+
}

0 commit comments

Comments
 (0)