diff --git a/java/java-impl/src/com/intellij/openapi/projectRoots/impl/SdkmanrcConfigurationProvider.kt b/java/java-impl/src/com/intellij/openapi/projectRoots/impl/SdkmanrcConfigurationProvider.kt index ddcad62d6018..3522e828ea84 100644 --- a/java/java-impl/src/com/intellij/openapi/projectRoots/impl/SdkmanrcConfigurationProvider.kt +++ b/java/java-impl/src/com/intellij/openapi/projectRoots/impl/SdkmanrcConfigurationProvider.kt @@ -14,16 +14,35 @@ public data class SdkmanReleaseData(val target: String, val flavour: String? = null, val vendor: String? = null) : JdkReleaseData { public companion object { - private val regex: Regex = Regex("(\\d+(?:\\.\\d+)*)(?:\\.([^-]+))?-?(\\S*)?") + private val versionRegex: Regex = Regex("\\d+(?:\\.\\d+)*") + private val vendorRegex: Regex = Regex("[a-z]+") + /** + * Parses a SDKMAN! java candidate identifier, as listed by `sdk list java`. + * + * An identifier consists of a dot-separated [version], an optional [flavour] holding the remaining + * qualifiers and build metadata (`fx`, `ea.11`, `hs`, `+1.1`, `r25`, ...), + * and an optional [vendor] suffix separated by the last `-`. + * + * For example: `8.0.504-amzn`, `28.0.0+ea.11-open`, `25.0.4-fx+1.1-librca`, `16.0.1.hs-adpt`. + */ public fun parse(text: String): SdkmanReleaseData? { - val matchResult = regex.matchEntire(text) ?: return null - return SdkmanReleaseData( - text, - matchResult.groups[1]?.value ?: return null, - matchResult.groups[2]?.value, - matchResult.groups[3]?.value - ) + if (text.isEmpty() || !text[0].isDigit()) return null + + var rest = text + var vendor: String? = null + val separatorIndex = text.lastIndexOf('-') + if (separatorIndex > 0) { + val suffix = text.substring(separatorIndex + 1) + if (vendorRegex.matches(suffix)) { + vendor = suffix + rest = text.substring(0, separatorIndex) + } + } + + val version = versionRegex.matchAt(rest, 0)?.value ?: return null + val flavour = rest.substring(version.length).trimStart('.', '-', '+').takeIf { it.isNotEmpty() } + return SdkmanReleaseData(text, version, flavour, vendor) } } @@ -39,14 +58,14 @@ public data class SdkmanReleaseData(val target: String, "graalce" -> JdkVersionDetector.Variant.GraalVMCE "jbr" -> JdkVersionDetector.Variant.JBR "kona" -> JdkVersionDetector.Variant.Kona - "librca" -> JdkVersionDetector.Variant.Liberica + "librca", "librcafx", "nik" -> JdkVersionDetector.Variant.Liberica "ms" -> JdkVersionDetector.Variant.Microsoft "oracle" -> JdkVersionDetector.Variant.Oracle "open" -> JdkVersionDetector.Variant.Oracle "sapmchn" -> JdkVersionDetector.Variant.SapMachine "sem" -> JdkVersionDetector.Variant.Semeru "tem" -> JdkVersionDetector.Variant.Temurin - "zulu" -> JdkVersionDetector.Variant.Zulu + "zulu", "zulufx" -> JdkVersionDetector.Variant.Zulu else -> JdkVersionDetector.Variant.Unknown } } diff --git a/java/java-tests/testSrc/com/intellij/openapi/projectRoots/impl/SdkmanrcWatcherTest.kt b/java/java-tests/testSrc/com/intellij/openapi/projectRoots/impl/SdkmanrcWatcherTest.kt index 82455994fc5d..ae847cbf2a9c 100644 --- a/java/java-tests/testSrc/com/intellij/openapi/projectRoots/impl/SdkmanrcWatcherTest.kt +++ b/java/java-tests/testSrc/com/intellij/openapi/projectRoots/impl/SdkmanrcWatcherTest.kt @@ -5,6 +5,7 @@ import com.intellij.platform.util.coroutines.childScope import com.intellij.testFramework.UsefulTestCase import kotlinx.coroutines.cancel import kotlinx.coroutines.runBlocking +import org.jetbrains.jps.model.java.JdkVersionDetector class SdkmanrcWatcherHeavyTests : ExternalJavaConfigurationTest() { override val mockJdkVersions: List = listOf( @@ -38,7 +39,7 @@ class SdkmanrcWatcherLightTests : UsefulTestCase() { fun `test candidates parsing`() { assertEquals(SdkmanReleaseData.parse("8"), - SdkmanReleaseData("8", "8", null, "")) + SdkmanReleaseData("8", "8", null, null)) assertEquals(SdkmanReleaseData.parse("19-zulu"), SdkmanReleaseData("19-zulu", "19", null, "zulu")) @@ -53,6 +54,60 @@ class SdkmanrcWatcherLightTests : UsefulTestCase() { SdkmanReleaseData("16.0.1.hs-adpt", "16.0.1", "hs", "adpt")) } + /** + * Identifiers as printed by `sdk list java`. + */ + fun `test identifiers parsing`() { + assertEquals(SdkmanReleaseData.parse("21.0.12+1.1-tem"), + SdkmanReleaseData("21.0.12+1.1-tem", "21.0.12", "1.1", "tem")) + + assertEquals(SdkmanReleaseData.parse("28.0.0+ea.11-open"), + SdkmanReleaseData("28.0.0+ea.11-open", "28.0.0", "ea.11", "open")) + + assertEquals(SdkmanReleaseData.parse("26.0.2-fx+1.1-librca"), + SdkmanReleaseData("26.0.2-fx+1.1-librca", "26.0.2", "fx+1.1", "librca")) + + assertEquals(SdkmanReleaseData.parse("26.0.2.fx-zulu"), + SdkmanReleaseData("26.0.2.fx-zulu", "26.0.2", "fx", "zulu")) + + assertEquals(SdkmanReleaseData.parse("11.0.14.1-jbr"), + SdkmanReleaseData("11.0.14.1-jbr", "11.0.14.1", null, "jbr")) + + assertEquals(SdkmanReleaseData.parse("25.0.4+1.1.r25-nik"), + SdkmanReleaseData("25.0.4+1.1.r25-nik", "25.0.4", "1.1.r25", "nik")) + + assertEquals(SdkmanReleaseData.parse("8.0.504+1-librca"), + SdkmanReleaseData("8.0.504+1-librca", "8.0.504", "1", "librca")) + + assertNull(SdkmanReleaseData.parse("current")) + } + + fun `test identifiers versions and variants`() { + for ((identifier, expected) in mapOf( + "26.0.2-amzn" to (JdkVersionDetector.Variant.Corretto to 26), + "25.2.4-graalce" to (JdkVersionDetector.Variant.GraalVMCE to 25), + "25.0.4-graal" to (JdkVersionDetector.Variant.GraalVM to 25), + "28.0.0+ea.11-open" to (JdkVersionDetector.Variant.Oracle to 28), + "25.0.4-jbr" to (JdkVersionDetector.Variant.JBR to 25), + "26.0.2-fx+1.1-librca" to (JdkVersionDetector.Variant.Liberica to 26), + "25.0.4+1.1.r25-nik" to (JdkVersionDetector.Variant.Liberica to 25), + "25.0.4+1-ms" to (JdkVersionDetector.Variant.Microsoft to 25), + "26.0.2-oracle" to (JdkVersionDetector.Variant.Oracle to 26), + "26.0.2+1-sapmchn" to (JdkVersionDetector.Variant.SapMachine to 26), + "26.0.2-sem" to (JdkVersionDetector.Variant.Semeru to 26), + "21.0.12+1.1-tem" to (JdkVersionDetector.Variant.Temurin to 21), + "25.0.4+1-kona" to (JdkVersionDetector.Variant.Kona to 25), + "26.0.2+1.1-zulu" to (JdkVersionDetector.Variant.Zulu to 26), + "8.0.504+1-librca" to (JdkVersionDetector.Variant.Liberica to 8), + )) { + val releaseData = SdkmanReleaseData.parse(identifier) + assertNotNull("$identifier is not parsed", releaseData) + val (variant, feature) = expected + assertEquals("$identifier is not parsed as $variant", variant, releaseData!!.variant) + assertEquals("$identifier is not parsed as Java $feature", feature, releaseData.javaVersion?.feature) + } + } + fun `test candidates matching`() { for ((candidate, version) in mapOf( "11.0.11.hs-adpt" to "AdoptOpenJDK (HotSpot) 11.0.11",