From 8b31dac5759e20d7c17a98f7ddc9c85d858f1591 Mon Sep 17 00:00:00 2001 From: Dusan Petrovic Date: Thu, 4 Jul 2024 16:50:52 +0200 Subject: [PATCH] Check for source level java compatibility Get java version from Maven projects Util for checking if extension command is available Move file Get java version from installation path Project abstraction with methos to retreive java version Initial position of breakpoints view is next to navigator Always open expression evaluator view PHP: removed built-in type real - deleted the built-in type real (php does not have a built-in real type: https://www.php.net/manual/en/language.types.intro.php Also see the warning under "Scalar Types": https://www.php.net/manual/en/language.types.declarations.php) - fixed broken tests - new tests added - fixes generation of return type real instead of float for a method when overriding a base class method with an autocomplete - fixes generation of return type real instead of float for a function in phpDoc Fix "Fix Imports" [GH-7546] - https://github.com/apache/netbeans/issues/7546 - Compare segments of namespace names to avoid adding incorrect items - Add a unit test Reduce redundant cast warnings. These happen as a side result of code cleanup.. Reduce warnings that look like this: [repeat] /home/bwalker/src/netbeans/enterprise/tomcat5/src/org/netbeans/modules/tomcat5/util/LogManager.java:271: warning: [cast] redundant cast to LogViewer [repeat] contextLog = (LogViewer)contextLogViewers.get(moduleID); [repeat] ^Cleanup redundant casts. These are a side effect of work done doing code cleanup. Fix unit tests - Use `checkCompletionOnlyDocumentation` test method because there are functions that have the same prefix Micronaut: Clear symbol cache on ClasspathInfo changes. Remove downloader from NBI - the download manager relied on suspend/resume/stop from java.lang.Thread which were deprecated since Java 1.2 - JDK 23 removes suspend/resume, others are already no-ops - fixing this would require a completely different approach, and since NBI is no longer maintained and in the process to be replaced by nbpackage this is th easiest option Remove redundant code Remove new lines Await on NbLanguageClient Removed unused code Reduce number of resolve(undefined) Explicitly set xml2js parser to be sync Shorten array element check Recursively search for subprojects Reference github issue inside comment Use vscode.Uri.file Modify regex to support whitespaces between identifiers, literals and symbols Use vscode.Uri.parse Removed explicit check for the command as no longer needed Add licence header NBI: disable update check again - Hide check-for-updates step from the (windows) installer again since it is a no-op with the current NB release model - reverts #1348 without changing codepaths outside the checkbox logic to reduce risks of other side effects - can be re-enabled using a property Docker - Load images Docker images don't load because `VirtualSize` attribute was removed from Docker Engine in v1.44. --- Using `getOrDefault` for the `Created`, `Size` and `VirtualSize` attributes. Using `Size` as default value to `VirtualSize`. Annotating the `VirtualSize` attribute as deprecated CI: start testing on 23-ea - move from 22 to 23-ea (all except java hints which would require nb-javac 23) - merge APISupport job into another job JDK 23 related test fixes - '-proc:full' is now required for annotation processing - java.lang.Thread finally lost a few methods Improving multistep infrastructure, command for uploading cloud assets to config map --- java/java.lsp.server/vscode/package-lock.json | 66 +++++- java/java.lsp.server/vscode/package.json | 4 +- java/java.lsp.server/vscode/src/extension.ts | 4 +- .../src/jdk/validation/extensionUtils.ts | 32 +++ .../vscode/src/jdk/validation/javaUtil.ts | 78 +++++++ .../vscode/src/jdk/validation/project.ts | 193 ++++++++++++++++++ .../vscode/src/jdk/validation/validation.ts | 65 ++++++ 7 files changed, 439 insertions(+), 3 deletions(-) create mode 100644 java/java.lsp.server/vscode/src/jdk/validation/extensionUtils.ts create mode 100644 java/java.lsp.server/vscode/src/jdk/validation/javaUtil.ts create mode 100644 java/java.lsp.server/vscode/src/jdk/validation/project.ts create mode 100644 java/java.lsp.server/vscode/src/jdk/validation/validation.ts diff --git a/java/java.lsp.server/vscode/package-lock.json b/java/java.lsp.server/vscode/package-lock.json index e2f2b8b89997..069cd9c289ed 100644 --- a/java/java.lsp.server/vscode/package-lock.json +++ b/java/java.lsp.server/vscode/package-lock.json @@ -13,7 +13,8 @@ "@vscode/webview-ui-toolkit": "^1.2.2", "jdk-utils": "^0.4.4", "jsonc-parser": "3.0.0", - "vscode-languageclient": "8.0.1" + "vscode-languageclient": "8.0.1", + "xml2js": "^0.6.2" }, "devDependencies": { "@types/glob": "^7.1.1", @@ -22,6 +23,7 @@ "@types/ps-node": "^0.1.0", "@types/vscode": "^1.76.0", "@types/vscode-webview": "^1.57.1", + "@types/xml2js": "^0.4.14", "@vscode/codicons": "0.0.29", "esbuild": "^0.16.17", "glob": "^7.1.6", @@ -463,6 +465,15 @@ "integrity": "sha512-ghW5SfuDmsGDS2A4xkvGsLwDRNc3Vj5rS6rPOyPm/IryZuf3wceZKxgYaUoW+k9f0f/CB7y2c1rRsdOWZWn0PQ==", "dev": true }, + "node_modules/@types/xml2js": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.14.tgz", + "integrity": "sha512-4YnrRemBShWRO2QjvUin8ESA41rH+9nQGLUGZV/1IDhi3SL9OhdpNC/MrulTWuptXKwhx/aDxE7toV0f/ypIXQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@ungap/promise-all-settled": { "version": "1.1.2", "dev": true, @@ -1372,6 +1383,11 @@ ], "license": "MIT" }, + "node_modules/sax": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" + }, "node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -1574,6 +1590,26 @@ "dev": true, "license": "ISC" }, + "node_modules/xml2js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", + "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "engines": { + "node": ">=4.0" + } + }, "node_modules/y18n": { "version": "5.0.8", "dev": true, @@ -1861,6 +1897,15 @@ "integrity": "sha512-ghW5SfuDmsGDS2A4xkvGsLwDRNc3Vj5rS6rPOyPm/IryZuf3wceZKxgYaUoW+k9f0f/CB7y2c1rRsdOWZWn0PQ==", "dev": true }, + "@types/xml2js": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.14.tgz", + "integrity": "sha512-4YnrRemBShWRO2QjvUin8ESA41rH+9nQGLUGZV/1IDhi3SL9OhdpNC/MrulTWuptXKwhx/aDxE7toV0f/ypIXQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@ungap/promise-all-settled": { "version": "1.1.2", "dev": true @@ -2445,6 +2490,11 @@ "version": "5.2.1", "dev": true }, + "sax": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" + }, "semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -2581,6 +2631,20 @@ "version": "1.0.2", "dev": true }, + "xml2js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", + "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + } + }, + "xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" + }, "y18n": { "version": "5.0.8", "dev": true diff --git a/java/java.lsp.server/vscode/package.json b/java/java.lsp.server/vscode/package.json index 625521ed55c4..f82349e990f5 100644 --- a/java/java.lsp.server/vscode/package.json +++ b/java/java.lsp.server/vscode/package.json @@ -1256,6 +1256,7 @@ "@types/ps-node": "^0.1.0", "@types/vscode": "^1.76.0", "@types/vscode-webview": "^1.57.1", + "@types/xml2js": "^0.4.14", "@vscode/codicons": "0.0.29", "esbuild": "^0.16.17", "glob": "^7.1.6", @@ -1269,7 +1270,8 @@ "@vscode/webview-ui-toolkit": "^1.2.2", "jdk-utils": "^0.4.4", "jsonc-parser": "3.0.0", - "vscode-languageclient": "8.0.1" + "vscode-languageclient": "8.0.1", + "xml2js": "^0.6.2" }, "__metadata": { "id": "66c7d7dc-934c-499b-94af-5375e8234fdd", diff --git a/java/java.lsp.server/vscode/src/extension.ts b/java/java.lsp.server/vscode/src/extension.ts index 149ae4894cbb..1852cfa9cdb1 100644 --- a/java/java.lsp.server/vscode/src/extension.ts +++ b/java/java.lsp.server/vscode/src/extension.ts @@ -62,12 +62,13 @@ import { InputStep, MultiStepInput } from './utils'; import { PropertiesView } from './propertiesView/propertiesView'; import * as configuration from './jdk/configuration'; import * as jdk from './jdk/jdk'; +import { validateJDKCompatibility } from './jdk/validation/validation'; const API_VERSION : string = "1.0"; export const COMMAND_PREFIX : string = "nbls"; const DATABASE: string = 'Database'; const listeners = new Map(); -let client: Promise; +export let client: Promise; let testAdapter: NbTestAdapter | undefined; let nbProcess : ChildProcess | null = null; let debugPort: number = -1; @@ -184,6 +185,7 @@ function findJDK(onChange: (path : string | null) => void): void { } let currentJdk = find(); + validateJDKCompatibility(currentJdk); let timeout: NodeJS.Timeout | undefined = undefined; workspace.onDidChangeConfiguration(params => { if (timeout) { diff --git a/java/java.lsp.server/vscode/src/jdk/validation/extensionUtils.ts b/java/java.lsp.server/vscode/src/jdk/validation/extensionUtils.ts new file mode 100644 index 000000000000..39d0f56ade0c --- /dev/null +++ b/java/java.lsp.server/vscode/src/jdk/validation/extensionUtils.ts @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import * as vscode from 'vscode'; +import { client as nblsClient } from '../../extension'; + +const RH_EXTENSION_ID = 'redhat.java'; + +export function isRHExtensionActive(): boolean { + const rh = vscode.extensions.getExtension(RH_EXTENSION_ID); + return rh ? true : false; +} + +export async function waitForNblsCommandToBeAvailable() { + await nblsClient; +} diff --git a/java/java.lsp.server/vscode/src/jdk/validation/javaUtil.ts b/java/java.lsp.server/vscode/src/jdk/validation/javaUtil.ts new file mode 100644 index 000000000000..af34a5270720 --- /dev/null +++ b/java/java.lsp.server/vscode/src/jdk/validation/javaUtil.ts @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import * as fs from 'fs'; +import * as path from 'path'; +import * as cp from 'child_process'; + +const JAVA_VERSION_REGEX = /version\s+"(\S+)"/; + +function findExecutable(program: string, home: string): string | undefined { + if (home) { + let executablePath = path.join(home, 'bin', program); + if (process.platform === 'win32') { + if (fs.existsSync(executablePath + '.cmd')) { + return executablePath + '.cmd'; + } + if (fs.existsSync(executablePath + '.exe')) { + return executablePath + '.exe'; + } + } else if (fs.existsSync(executablePath)) { + return executablePath; + } + } + return undefined; +} + +export function normalizeJavaVersion(version: string): string { + return version.startsWith("1.") ? version.substring(2) : version; +} + +export async function getJavaVersion(homeFolder: string): Promise { + return new Promise(resolve => { + if (homeFolder && fs.existsSync(homeFolder)) { + const executable: string | undefined = findExecutable('java', homeFolder); + if (executable) { + cp.execFile(executable, ['-version'], { encoding: 'utf8' }, (_error, _stdout, stderr) => { + if (stderr) { + let javaVersion: string | undefined; + stderr.split('\n').forEach((line: string) => { + const javaInfo: string[] | null = line.match(JAVA_VERSION_REGEX); + if (javaInfo && javaInfo.length > 1) { + javaVersion = javaInfo[1]; + } + }); + if (javaVersion) { + let majorVersion = normalizeJavaVersion(javaVersion); + let i = majorVersion.indexOf('.'); + if (i > -1) { + majorVersion = majorVersion.slice(0, i); + } + resolve(majorVersion); + return; + } + } + resolve(undefined); + }); + } + } else { + resolve(undefined); + } + }); +} \ No newline at end of file diff --git a/java/java.lsp.server/vscode/src/jdk/validation/project.ts b/java/java.lsp.server/vscode/src/jdk/validation/project.ts new file mode 100644 index 000000000000..57419e2e5eb5 --- /dev/null +++ b/java/java.lsp.server/vscode/src/jdk/validation/project.ts @@ -0,0 +1,193 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import * as xml2js from 'xml2js'; +import * as fs from 'fs'; +import * as path from 'path'; +import * as vscode from 'vscode'; +import * as os from 'os'; +import { normalizeJavaVersion } from './javaUtil'; + +const GET_PROJECT_INFO = 'nbls.project.info'; +const GRADLE_TARGET_COMPATIBILITY_REGEX = /targetCompatibility\s*=\s*(?:JavaVersion\s*\.\s*toVersion\s*\(\s*['"](\d+(\.\d+)?)['"]\s*\)|['"](\d+(\.\d+)?)['"])/ +const GRADLE_SOURCE_COMPATIBILITY_REGEX = /sourceCompatibility\s*=\s*(?:JavaVersion\s*\.\s*toVersion\s*\(\s*['"](\d+(\.\d+)?)['"]\s*\)|['"](\d+(\.\d+)?)['"])/ + +export enum BuildSystemType { + MAVEN = 'Maven', + GRADLE = 'Gradle', + UNKNOWN = 'Unknown' +} + +export async function getProjectFrom(projectUri: vscode.Uri): Promise { + const projectInfos: any[] = await vscode.commands.executeCommand(GET_PROJECT_INFO, projectUri.toString(), { recursive: true, projectStructure: true }); + if (projectInfos?.length && projectInfos[0]) { + const projectDirectory = projectInfos[0].projectDirectory.toString(); + const buildSystem: BuildSystemType = resolveBuildSystemType(projectUri, projectInfos[0].projectType); + + switch (buildSystem) { + case BuildSystemType.MAVEN: + const mavenSubprojects: Project[] = projectInfos[0].subprojects + .map((subproject: string) => new MavenProject(subproject, [])) + return new MavenProject(projectDirectory, mavenSubprojects); + case BuildSystemType.GRADLE: + const gradleSubprojects: Project[] = projectInfos[0].subprojects + .map((subproject: string) => new GradleProject(subproject, [])) + return new GradleProject(projectDirectory, gradleSubprojects); + default: + break; + } + } + return Promise.resolve(undefined); +} + +function resolveBuildSystemType(uri: vscode.Uri, projectType?: string): BuildSystemType { + if (projectType?.includes('gradle')) { + return BuildSystemType.GRADLE; + } + if (projectType?.includes('maven')) { + return BuildSystemType.MAVEN; + } + if (fs.existsSync(path.join(uri.fsPath, 'build.gradle'))) { + return BuildSystemType.GRADLE; + } + if (fs.existsSync(path.join(uri.fsPath, 'pom.xml'))) { + return BuildSystemType.MAVEN; + } + return BuildSystemType.UNKNOWN; +} + +export abstract class Project { + + readonly directory: string; + readonly subprojects: Project[]; + + constructor(directory: string, subprojects: any[]) { + this.directory = vscode.Uri.parse(directory).fsPath; + this.subprojects = subprojects; + } + + // Whether the project contains subprojects + containsSubprojects(): boolean { + return this.subprojects.length > 0; + } + + async getJavaVersion(): Promise { + if (!this.containsSubprojects()) { + return this.extractJavaVersion(); + } + + let maxJavaVersion: number | undefined; + + for (const subproject of this.subprojects) { + const projectDirectory: string = vscode.Uri.file(subproject.directory).toString(); + const subInfos: any[] = await vscode.commands.executeCommand(GET_PROJECT_INFO, projectDirectory); + if (subInfos?.length && subInfos[0]) { + const javaVersion = subproject.extractJavaVersion(); + + if (!maxJavaVersion || (javaVersion && javaVersion > maxJavaVersion)) { + maxJavaVersion = javaVersion; + } + } + } + return maxJavaVersion; + } + + // Extracts project java version + // Note: update when this feature becomes available: https://github.com/apache/netbeans/issues/7557 + protected abstract extractJavaVersion(): number | undefined +} + +export class MavenProject extends Project { + + constructor(directory: string, subprojects: any[]) { + super(directory, subprojects); + } + + extractJavaVersion(): number | undefined { + const buildscript = path.resolve(this.directory, 'pom.xml'); + let version: string | undefined; + if (fs.existsSync(buildscript)) { + const parser: xml2js.Parser = new xml2js.Parser({ async: false }); + parser.parseString(fs.readFileSync(buildscript)?.toString() || '', (err, result) => { + if (!err && result) { + const properties = result['project']?.['properties']; + if (properties?.[0]) { + const mavenCompilerTarget = properties[0]['maven.compiler.target']; + if (mavenCompilerTarget?.[0]) { + version = mavenCompilerTarget[0]; + return; + } + + const mavenCompilerSource = properties[0]['maven.compiler.source']; + if (mavenCompilerSource?.[0]) { + version = mavenCompilerSource[0]; + return; + } + + const jdkVersion = properties[0]['jdk.version']; + if (jdkVersion?.[0]) { + version = jdkVersion[0]; + return; + } + } + } + }) + } + return version ? Number(normalizeJavaVersion(version)) : undefined; + } +} + +export class GradleProject extends Project { + + constructor(directory: string, subprojects: any[]) { + super(directory, subprojects); + } + + getJavaCompatibilityFrom(buildscript: string, from: 'target' | 'source'): string | undefined { + const res = from === 'target' ? GRADLE_TARGET_COMPATIBILITY_REGEX.exec(buildscript) : GRADLE_SOURCE_COMPATIBILITY_REGEX.exec(buildscript) + if (res?.[3]) { + return res[3]; // Get the version number directly + } else if (res?.[1]) { + return res[1]; // Get the version number from JavaVersion.toVersion + } + return undefined; + } + + extractJavaVersion(): number | undefined { + let version: number | undefined; + const buildscript = path.resolve(this.directory, 'build.gradle'); + if (fs.existsSync(buildscript)) { + fs.readFileSync(buildscript)?.toString().split(os.EOL).find(l => { + let tempVersion: string | undefined = this.getJavaCompatibilityFrom(l, 'target'); + + if (!tempVersion) { + tempVersion = this.getJavaCompatibilityFrom(l, 'source'); + } + + if (tempVersion) { + version = Number(normalizeJavaVersion(tempVersion)); + return true; + } + + return false; + }); + } + return version; + } +} \ No newline at end of file diff --git a/java/java.lsp.server/vscode/src/jdk/validation/validation.ts b/java/java.lsp.server/vscode/src/jdk/validation/validation.ts new file mode 100644 index 000000000000..fbebedc1555b --- /dev/null +++ b/java/java.lsp.server/vscode/src/jdk/validation/validation.ts @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import * as vscode from 'vscode'; +import * as jdkUtils from 'jdk-utils'; + +import { isRHExtensionActive, waitForNblsCommandToBeAvailable } from './extensionUtils'; +import { getJavaVersion } from './javaUtil'; +import { getProjectFrom } from './project'; + +const CONFIGURE_JDK_COMMAND = 'nbls.jdk.configuration' +const CONFIGURE_JDK = 'Configure JDK'; + +export async function validateJDKCompatibility(javaPath: string | null) { + // In this case RH will try it's best to validate Java versions + if (isRHExtensionActive()) return; + + const projectJavaVersion = await getProjectJavaVersion(); + const ideJavaVersion = await parseJavaVersion(javaPath); + if (projectJavaVersion && ideJavaVersion && ideJavaVersion < projectJavaVersion) { + const value = await vscode.window.showWarningMessage(`Source level (JDK ${projectJavaVersion}) not compatible with current JDK installation (JDK ${ideJavaVersion})`, CONFIGURE_JDK); + if (value === CONFIGURE_JDK) { + vscode.commands.executeCommand(CONFIGURE_JDK_COMMAND); + } + } +} + +async function parseJavaVersion(javaPath: string | null): Promise { + if (!javaPath) return undefined; + + const javaRuntime = await jdkUtils.getRuntime(javaPath, { checkJavac: true }); + if (!javaRuntime?.hasJavac) { + return undefined; + } + const version = await getJavaVersion(javaRuntime.homedir); + return version ? Number(version) : undefined; +} + +async function getProjectJavaVersion(): Promise { + const folder = vscode.workspace.workspaceFolders?.[0]; + if (!folder) return undefined; + + await waitForNblsCommandToBeAvailable(); + + const project = await getProjectFrom(folder.uri); + const javaVersion = await project?.getJavaVersion(); + + return javaVersion; +}