diff --git a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/DelegatingStreamConnectionProvider.java b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/DelegatingStreamConnectionProvider.java index a501b3dfc6..76deaa515a 100644 --- a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/DelegatingStreamConnectionProvider.java +++ b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/DelegatingStreamConnectionProvider.java @@ -40,6 +40,7 @@ import org.eclipse.lsp4j.services.LanguageServer; import org.eclipse.ui.PlatformUI; import org.osgi.framework.Bundle; +import org.springframework.ide.eclipse.editor.support.preferences.ProblemSeverityPreferityPageFromMetadata; import org.springframework.tooling.boot.ls.prefs.CategoryProblemsSeverityPrefsPage; import org.springframework.tooling.boot.ls.prefs.FileListEditor; import org.springframework.tooling.boot.ls.prefs.ProblemCategoryData; @@ -328,6 +329,15 @@ private void putValidationCategoryToggles(Map settings) { try { IEclipsePreferences prefs = BootLanguageServerPlugin.getPreferences(); for (ProblemCategoryData category : CategoryProblemsSeverityPrefsPage.ALL_PROBLEM_CATEGORIES) { + if (category.getParameters() != null) { + for (ProblemSeverityPreferityPageFromMetadata.ProblemParameterData param : category.getParameters()) { + String prefKey = "problem-parameters." + category.getId() + "." + param.getKey(); + String val = prefs.get(prefKey, null); + if (val != null) { + dotPut(settings, "spring-boot.ls." + prefKey, val); + } + } + } if (category.getToggle() != null) { CategoryToggleData toggle = category.getToggle(); String val = prefs.get(toggle.getPreferenceKey(), null); diff --git a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/prefs/CategoryProblemsSeverityPrefsPage.java b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/prefs/CategoryProblemsSeverityPrefsPage.java index 95168ecac5..367b2343d7 100644 --- a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/prefs/CategoryProblemsSeverityPrefsPage.java +++ b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/prefs/CategoryProblemsSeverityPrefsPage.java @@ -15,9 +15,12 @@ import org.eclipse.core.runtime.preferences.DefaultScope; import org.eclipse.core.runtime.preferences.IEclipsePreferences; +import org.eclipse.jface.preference.BooleanFieldEditor; import org.eclipse.jface.preference.ComboFieldEditor; +import org.eclipse.jface.preference.IntegerFieldEditor; import org.eclipse.jface.preference.PreferenceManager; import org.eclipse.jface.preference.PreferenceNode; +import org.eclipse.jface.preference.StringFieldEditor; import org.eclipse.ui.PlatformUI; import org.springframework.ide.eclipse.editor.support.preferences.ProblemSeverityPreferencesUtil; import org.springframework.ide.eclipse.editor.support.preferences.ProblemSeverityPreferityPageFromMetadata; @@ -57,6 +60,12 @@ protected void initializeDefaults() { defaults.put(PREF_KEY_PREFIX + category.getToggle().getPreferenceKey(), category.getToggle().getDefaultValue()); } + if (category.getParameters() != null) { + IEclipsePreferences defaults = DefaultScope.INSTANCE.getNode(getPluginId()); + for (ProblemParameterData param : category.getParameters()) { + defaults.put(getProblemParametersPreferencePrefix() + param.getKey(), param.getDefaultValue()); + } + } super.initializeDefaults(); } @@ -72,6 +81,18 @@ protected void createFieldEditors() { ); addField(field); } + if (category.getParameters() != null) { + for (ProblemParameterData param : category.getParameters()) { + String prefKey = getProblemParametersPreferencePrefix() + param.getKey(); + if ("boolean".equals(param.getType())) { + addField(new BooleanFieldEditor(prefKey, param.getLabel(), getFieldEditorParent())); + } else if ("integer".equals(param.getType())) { + addField(new IntegerFieldEditor(prefKey, param.getLabel(), getFieldEditorParent())); + } else { + addField(new StringFieldEditor(prefKey, param.getLabel(), getFieldEditorParent())); + } + } + } super.createFieldEditors(); } diff --git a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/prefs/ProblemCategoryData.java b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/prefs/ProblemCategoryData.java index 9ee758116b..9fc9047e62 100644 --- a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/prefs/ProblemCategoryData.java +++ b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/prefs/ProblemCategoryData.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2022 VMware, Inc. + * Copyright (c) 2022, 2026 VMware, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -10,6 +10,7 @@ *******************************************************************************/ package org.springframework.tooling.boot.ls.prefs; +import org.springframework.ide.eclipse.editor.support.preferences.ProblemSeverityPreferityPageFromMetadata.ProblemParameterData; import org.springframework.ide.eclipse.editor.support.preferences.ProblemSeverityPreferityPageFromMetadata.ProblemTypeData; public class ProblemCategoryData { @@ -19,6 +20,7 @@ public class ProblemCategoryData { private CategoryToggleData toggle; private int order; private ProblemTypeData[] problemTypes; + private ProblemParameterData[] parameters; ProblemCategoryData() {} @@ -42,6 +44,10 @@ public String getId() { return id; } + public ProblemParameterData[] getParameters() { + return parameters; + } + public ProblemTypeData[] getProblemTypes() { return problemTypes; } diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/reconcile/ProblemCategory.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/reconcile/ProblemCategory.java index a0430676b5..303c0aa004 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/reconcile/ProblemCategory.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/reconcile/ProblemCategory.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2022 VMware, Inc. + * Copyright (c) 2022, 2026 VMware, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -28,13 +28,21 @@ public final class ProblemCategory { final private List problemTypes = new ArrayList<>();; final private Toggle toggle; + private List parameters; final public int order; public ProblemCategory(String id, String label, Toggle toggle) { + this(id, label, toggle, null); + } + + public ProblemCategory(String id, String label, Toggle toggle, List parameters) { this.id = id; this.label = label; this.toggle = toggle; + if (parameters != null) { + this.parameters = new ArrayList<>(parameters); + } this.order = counter.getAndIncrement(); } @@ -50,6 +58,10 @@ public List getProblemTypes() { return problemTypes; } + public List getParameters() { + return parameters; + } + public Toggle getToggle() { return toggle; } diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/reconcile/ProblemType.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/reconcile/ProblemType.java index d7e0494978..f51f2b0c0f 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/reconcile/ProblemType.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/reconcile/ProblemType.java @@ -40,6 +40,6 @@ public interface ProblemType { * {@code spring-boot.ls.problem-parameters.<category>.<code>.<key>} in LSP settings. */ default List getParameters() { - return ProblemTypeParameter.none(); + return null; } } diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/reconcile/ProblemTypeParameter.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/reconcile/ProblemTypeParameter.java index 5aa65434a5..6f507d31c3 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/reconcile/ProblemTypeParameter.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/reconcile/ProblemTypeParameter.java @@ -10,8 +10,6 @@ *******************************************************************************/ package org.springframework.ide.vscode.commons.languageserver.reconcile; -import java.util.List; - /** * Optional configuration for a {@link ProblemType}, surfaced in client settings and * {@code problem-types.json} metadata. @@ -58,7 +56,4 @@ public String getDefaultValue() { return defaultValue; } - public static List none() { - return List.of(); - } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootJavaConfig.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootJavaConfig.java index 7c06159fea..a547b7794a 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootJavaConfig.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootJavaConfig.java @@ -137,6 +137,11 @@ public boolean isSpelExpressionValidationEnabled() { return isProblemCategoryEnabled(categorySwitch); } + public boolean isUseProjectBuildFileForVersionValidation() { + Boolean enabled = settings.getBoolean("spring-boot", "ls", "problem-parameters", "version-validation", "use-project-build-file"); + return enabled == null || enabled.booleanValue(); + } + public boolean isBootVersionValidationEnabled() { Toggle categorySwitch = SpringProblemCategories.VERSION_VALIDATION.getToggle(); return isProblemCategoryEnabled(categorySwitch); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerInitializer.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerInitializer.java index 44b5877422..4ac53f9c7a 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerInitializer.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerInitializer.java @@ -131,7 +131,7 @@ public void afterPropertiesSet() throws Exception { new BootJavaLanguageServerComponents(appContext), new SpringXMLLanguageServerComponents(server, springIndexer, params, config, appContext.getBean(SpelReconciler.class)), new SpringFactoriesLanguageServerComponents(projectFinder, springIndexer, config), - new PomLanguageServerComponents(server, projectFinder, params.projectObserver, appContext.getBean(SpringProjectsProvider.class), appContext.getBean(MavenMetadataProvider.class)), + new PomLanguageServerComponents(server, projectFinder, params.projectObserver, appContext.getBean(SpringProjectsProvider.class), appContext.getBean(MavenMetadataProvider.class), appContext.getBean(BootJavaConfig.class)), new JpaQueryPropertiesLanguageServerComponents(server.getTextDocumentService(), projectFinder, appContext.getBean(JpqlSemanticTokens.class), appContext.getBean(HqlSemanticTokens.class), appContext.getBean(JpqlSupportState.class), (Reconciler) appContext.getBean("hqlReconciler"), (Reconciler) appContext.getBean("jpqlReconciler")) ); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootVersionValidationConfig.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootVersionValidationConfig.java index f245181ecd..114f9bc245 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootVersionValidationConfig.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootVersionValidationConfig.java @@ -45,8 +45,8 @@ public class BootVersionValidationConfig { return new MavenMetadataProvider(server.getWorkspaceService().getFileObserver(), server.getProgressService()); } - @Bean UpdateBootVersion updateBootVersion(SimpleLanguageServer server, Optional bootUpgradeOpt, SpringProjectsProvider projectsProvider, MavenMetadataProvider mavenMetadataProvider) { - return new UpdateBootVersion(server.getDiagnosticSeverityProvider(), bootUpgradeOpt, projectsProvider, mavenMetadataProvider); + @Bean UpdateBootVersion updateBootVersion(SimpleLanguageServer server, Optional bootUpgradeOpt, SpringProjectsProvider projectsProvider, MavenMetadataProvider mavenMetadataProvider, BootJavaConfig bootJavaConfig) { + return new UpdateBootVersion(server.getDiagnosticSeverityProvider(), bootUpgradeOpt, projectsProvider, mavenMetadataProvider, bootJavaConfig); } @Bean SpringIoProjectsProvider springProjectsProvider(SimpleLanguageServer server, BootJavaConfig config, RestTemplateFactory restTemplateFactory) { diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/ProblemParameterProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/ProblemParameterProvider.java index 925545aa21..5779a82932 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/ProblemParameterProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/ProblemParameterProvider.java @@ -50,9 +50,11 @@ public int getIntParameter(ProblemType problem, String paramKey) { if (v != null) { return v; } - for (ProblemTypeParameter p : problem.getParameters()) { - if (paramKey.equals(p.getKey()) && p.getType() == ValueType.INTEGER) { - return Integer.parseInt(p.getDefaultValue()); + if (problem.getParameters() != null) { + for (ProblemTypeParameter p : problem.getParameters()) { + if (paramKey.equals(p.getKey()) && p.getType() == ValueType.INTEGER) { + return Integer.parseInt(p.getDefaultValue()); + } } } throw new IllegalStateException("No integer parameter '" + paramKey + "' for " + problem.getCode()); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/common/SpringProblemCategories.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/common/SpringProblemCategories.java index b8824926e3..bba62aa6e9 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/common/SpringProblemCategories.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/common/SpringProblemCategories.java @@ -14,6 +14,8 @@ import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemCategory; import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemCategory.Toggle; +import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemTypeParameter; +import java.util.List; import static org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemCategory.Toggle.Option.*; @@ -39,7 +41,8 @@ public class SpringProblemCategories { new Toggle("Enablement", EnumSet.of(OFF, ON), ON, "boot-java.validation.spel.on")); public static final ProblemCategory VERSION_VALIDATION = new ProblemCategory("version-validation", "Versions and Support Ranges", - new Toggle("Enablement", EnumSet.of(OFF, ON), ON, "boot-java.validation.java.version-validation")); + new Toggle("Enablement", EnumSet.of(OFF, ON), ON, "boot-java.validation.java.version-validation"), + List.of(new ProblemTypeParameter("use-project-build-file", "Use project build file for version validation", "Use project build file for version validation", ProblemTypeParameter.ValueType.BOOLEAN, "true"))); public static final ProblemCategory DATA_QUERY = new ProblemCategory("data-query", "Data Queries", new Toggle("Enablement", EnumSet.of(OFF, ON), ON, "boot-java.validation.data-query")); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/SpringAiProblemType.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/SpringAiProblemType.java index d385cb471a..8fd17a938e 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/SpringAiProblemType.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/SpringAiProblemType.java @@ -26,7 +26,7 @@ public enum SpringAiProblemType implements ProblemType { SPRING_AI_TOOL_MISSING_DESCRIPTION(WARNING, "Spring AI tool/prompt/resource is missing a description. Descriptions are critical for the LLM to correctly select and invoke the tool.", "Missing @Tool description", - ProblemTypeParameter.none()), + null), SPRING_AI_TOOL_DESCRIPTION_TOO_SHORT(WARNING, "Spring AI tool/prompt/resource description is too short. A meaningful description helps the LLM decide when and how to invoke the tool.", diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/maven/PomInlayHintHandler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/maven/PomInlayHintHandler.java index d283dbdd77..c414591962 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/maven/PomInlayHintHandler.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/maven/PomInlayHintHandler.java @@ -33,6 +33,7 @@ import org.eclipse.lsp4j.jsonrpc.CancelChecker; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.ide.vscode.boot.app.BootJavaConfig; import org.springframework.ide.vscode.boot.java.rewrite.SpringBootUpgrade; import org.springframework.ide.vscode.boot.validation.generations.GenerationsValidator; import org.springframework.ide.vscode.boot.validation.generations.MavenMetadata; @@ -49,6 +50,7 @@ import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver; import org.springframework.ide.vscode.commons.languageserver.util.InlayHintHandler; import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; +import org.springframework.ide.vscode.commons.protocol.java.ProjectBuild; import org.springframework.ide.vscode.commons.util.BadLocationException; import org.springframework.ide.vscode.commons.util.Optionals; import org.springframework.ide.vscode.commons.util.text.TextDocument; @@ -62,11 +64,13 @@ public class PomInlayHintHandler implements InlayHintHandler { final private JavaProjectFinder projectFinder; final private SpringProjectsProvider generationsProvider; final private MavenMetadataProvider mavenMetadataProvider; + final private BootJavaConfig bootJavaConfig; - public PomInlayHintHandler(SimpleLanguageServer server, JavaProjectFinder projectFinder, ProjectObserver projectObserver, SpringProjectsProvider generationsProvider, MavenMetadataProvider mavenMetadataProvider) { + public PomInlayHintHandler(SimpleLanguageServer server, JavaProjectFinder projectFinder, ProjectObserver projectObserver, SpringProjectsProvider generationsProvider, MavenMetadataProvider mavenMetadataProvider, BootJavaConfig bootJavaConfig) { this.projectFinder = projectFinder; this.generationsProvider = generationsProvider; this.mavenMetadataProvider = mavenMetadataProvider; + this.bootJavaConfig = bootJavaConfig; projectObserver.addListener(new ProjectObserver.Listener() { @@ -131,7 +135,7 @@ public List handle(TextDocument doc, Range range, CancelChecker cance try { SortedVersions versions = null; - if (org.springframework.ide.vscode.commons.protocol.java.ProjectBuild.MAVEN_PROJECT_TYPE.equals(jp.getProjectBuild().getType())) { + if (bootJavaConfig.isUseProjectBuildFileForVersionValidation() && ProjectBuild.MAVEN_PROJECT_TYPE.equals(jp.getProjectBuild().getType())) { try { MavenMetadata metadata = mavenMetadataProvider.getMetadata(jp, "org.springframework.boot", "spring-boot"); if (metadata != null) { diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/maven/PomLanguageServerComponents.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/maven/PomLanguageServerComponents.java index 68ce8a0ed0..d70cef094d 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/maven/PomLanguageServerComponents.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/maven/PomLanguageServerComponents.java @@ -13,6 +13,8 @@ import java.util.Optional; import java.util.Set; +import org.springframework.ide.vscode.boot.app.BootJavaConfig; +import org.springframework.ide.vscode.boot.validation.generations.MavenMetadataProvider; import org.springframework.ide.vscode.boot.validation.generations.SpringProjectsProvider; import org.springframework.ide.vscode.commons.languageserver.composable.LanguageServerComponents; import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; @@ -25,8 +27,8 @@ public class PomLanguageServerComponents implements LanguageServerComponents{ private PomInlayHintHandler inlayHintHandler; - public PomLanguageServerComponents(SimpleLanguageServer server, JavaProjectFinder projectFinder, ProjectObserver projectObserver, SpringProjectsProvider generationsProvider, org.springframework.ide.vscode.boot.validation.generations.MavenMetadataProvider mavenMetadataProvider) { - this.inlayHintHandler = new PomInlayHintHandler(server, projectFinder, projectObserver, generationsProvider, mavenMetadataProvider); + public PomLanguageServerComponents(SimpleLanguageServer server, JavaProjectFinder projectFinder, ProjectObserver projectObserver, SpringProjectsProvider generationsProvider, MavenMetadataProvider mavenMetadataProvider, BootJavaConfig bootJavaConfig) { + this.inlayHintHandler = new PomInlayHintHandler(server, projectFinder, projectObserver, generationsProvider, mavenMetadataProvider, bootJavaConfig); } @Override diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/UpdateBootVersion.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/UpdateBootVersion.java index ad8770a11e..45b1015ec0 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/UpdateBootVersion.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/UpdateBootVersion.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2023, 2025 VMware, Inc. + * Copyright (c) 2023, 2026 VMware, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -21,6 +21,7 @@ import org.eclipse.lsp4j.Diagnostic; import org.eclipse.lsp4j.Range; import org.eclipse.lsp4j.ShowDocumentParams; +import org.springframework.ide.vscode.boot.app.BootJavaConfig; import org.springframework.ide.vscode.boot.app.BootLanguageServerInitializer; import org.springframework.ide.vscode.boot.java.rewrite.SpringBootUpgrade; import org.springframework.ide.vscode.boot.validation.generations.preferences.VersionValidationProblemType; @@ -40,19 +41,21 @@ public class UpdateBootVersion extends AbstractDiagnosticValidator { private SpringProjectsProvider springProjectsProvider; private MavenMetadataProvider mavenMetadataProvider; + private BootJavaConfig bootJavaConfig; - public UpdateBootVersion(DiagnosticSeverityProvider diagnosticSeverityProvider, Optional bootUpgradeOpt, SpringProjectsProvider springProjectsProvider, MavenMetadataProvider mavenMetadataProvider) { + public UpdateBootVersion(DiagnosticSeverityProvider diagnosticSeverityProvider, Optional bootUpgradeOpt, SpringProjectsProvider springProjectsProvider, MavenMetadataProvider mavenMetadataProvider, BootJavaConfig bootJavaConfig) { super(diagnosticSeverityProvider); this.bootUpgradeOpt = bootUpgradeOpt; this.springProjectsProvider = springProjectsProvider; this.mavenMetadataProvider = mavenMetadataProvider; + this.bootJavaConfig = bootJavaConfig; } @Override public Collection validate(IJavaProject javaProject, Version javaProjectVersion) throws Exception { SortedVersions versions = null; - if (ProjectBuild.MAVEN_PROJECT_TYPE.equals(javaProject.getProjectBuild().getType())) { + if (bootJavaConfig.isUseProjectBuildFileForVersionValidation() && ProjectBuild.MAVEN_PROJECT_TYPE.equals(javaProject.getProjectBuild().getType())) { try { MavenMetadata metadata = mavenMetadataProvider.getMetadata(javaProject, "org.springframework.boot", "spring-boot"); if (metadata != null) { diff --git a/headless-services/spring-boot-language-server/src/main/resources/problem-types.json b/headless-services/spring-boot-language-server/src/main/resources/problem-types.json index cad35e3449..ee7f9ff268 100644 --- a/headless-services/spring-boot-language-server/src/main/resources/problem-types.json +++ b/headless-services/spring-boot-language-server/src/main/resources/problem-types.json @@ -444,6 +444,15 @@ "preferenceKey": "boot-java.validation.java.version-validation", "defaultValue": "ON" }, + "parameters": [ + { + "key": "use-project-build-file", + "label": "Use project build file for version validation", + "description": "Use project build file for version validation", + "type": "boolean", + "defaultValue": "true" + } + ], "order": 8, "problemTypes": [ { diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/maven/PomInlayHintHandlerTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/maven/PomInlayHintHandlerTest.java index ccbdd7fbf7..56b16aaf16 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/maven/PomInlayHintHandlerTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/maven/PomInlayHintHandlerTest.java @@ -10,9 +10,9 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.maven; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; @@ -25,28 +25,28 @@ import java.util.List; import java.util.Optional; -import org.springframework.ide.vscode.commons.java.IClasspath; -import org.springframework.ide.vscode.commons.java.IJavaProject; -import org.springframework.ide.vscode.commons.java.IProjectBuild; -import org.springframework.ide.vscode.commons.protocol.java.Classpath.CPE; - import org.eclipse.lsp4j.Command; import org.eclipse.lsp4j.InlayHint; import org.eclipse.lsp4j.InlayHintLabelPart; import org.eclipse.lsp4j.Position; import org.eclipse.lsp4j.jsonrpc.CancelChecker; import org.junit.jupiter.api.Test; +import org.springframework.ide.vscode.boot.app.BootJavaConfig; import org.springframework.ide.vscode.boot.validation.generations.MavenMetadataProvider; import org.springframework.ide.vscode.boot.validation.generations.SpringProjectsProvider; import org.springframework.ide.vscode.boot.validation.generations.json.Generation; import org.springframework.ide.vscode.boot.validation.generations.json.ResolvedSpringProject; import org.springframework.ide.vscode.commons.Version; +import org.springframework.ide.vscode.commons.java.IClasspath; +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.java.IProjectBuild; import org.springframework.ide.vscode.commons.java.SpringProjectUtil; import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver; import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService; import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject; +import org.springframework.ide.vscode.commons.protocol.java.Classpath.CPE; import org.springframework.ide.vscode.commons.util.text.LanguageId; import org.springframework.ide.vscode.commons.util.text.TextDocument; import org.springframework.ide.vscode.project.harness.ProjectsHarness; @@ -82,7 +82,7 @@ void inlayProvided() throws Exception { when(projectProvider.getProject(SpringProjectUtil.SPRING_BOOT)).thenReturn(resolvedProject); MavenMetadataProvider mavenMetadataProvider = mock(MavenMetadataProvider.class); - PomInlayHintHandler inlayHandler = new PomInlayHintHandler(server, projectFinder, ProjectObserver.NULL, projectProvider, mavenMetadataProvider); + PomInlayHintHandler inlayHandler = new PomInlayHintHandler(server, projectFinder, ProjectObserver.NULL, projectProvider, mavenMetadataProvider, new BootJavaConfig(server)); List hints = inlayHandler.handle(doc, doc.toRange(0, doc.getLength()), mock(CancelChecker.class)); @@ -128,7 +128,7 @@ void inlayNotProvidedOutOfOssSupport() throws Exception { when(projectProvider.getProject(SpringProjectUtil.SPRING_BOOT)).thenReturn(resolvedProject); MavenMetadataProvider mavenMetadataProvider = mock(MavenMetadataProvider.class); - PomInlayHintHandler inlayHandler = new PomInlayHintHandler(server, projectFinder, ProjectObserver.NULL, projectProvider, mavenMetadataProvider); + PomInlayHintHandler inlayHandler = new PomInlayHintHandler(server, projectFinder, ProjectObserver.NULL, projectProvider, mavenMetadataProvider, new BootJavaConfig(server)); List hints = inlayHandler.handle(doc, doc.toRange(0, doc.getLength()), mock(CancelChecker.class)); assertEquals(0, hints.size()); @@ -165,7 +165,7 @@ void upgradePatchVersionInlay() throws Exception { when(projectProvider.getProject(SpringProjectUtil.SPRING_BOOT)).thenReturn(resolvedProject); MavenMetadataProvider mavenMetadataProvider = mock(MavenMetadataProvider.class); - PomInlayHintHandler inlayHandler = new PomInlayHintHandler(server, projectFinder, ProjectObserver.NULL, projectProvider, mavenMetadataProvider); + PomInlayHintHandler inlayHandler = new PomInlayHintHandler(server, projectFinder, ProjectObserver.NULL, projectProvider, mavenMetadataProvider, new BootJavaConfig(server)); List hints = inlayHandler.handle(doc, doc.toRange(0, doc.getLength()), mock(CancelChecker.class)); assertEquals(1, hints.size()); @@ -222,7 +222,7 @@ void noInlayHintOnEmptyVersionTag() throws Exception { when(projectProvider.getProject(SpringProjectUtil.SPRING_BOOT)).thenReturn(resolvedProject); MavenMetadataProvider mavenMetadataProvider = mock(MavenMetadataProvider.class); - PomInlayHintHandler inlayHandler = new PomInlayHintHandler(server, projectFinder, ProjectObserver.NULL, projectProvider, mavenMetadataProvider); + PomInlayHintHandler inlayHandler = new PomInlayHintHandler(server, projectFinder, ProjectObserver.NULL, projectProvider, mavenMetadataProvider, new BootJavaConfig(server)); List hints = inlayHandler.handle(doc, doc.toRange(0, doc.getLength()), mock(CancelChecker.class)); assertEquals(0, hints.size()); @@ -264,7 +264,7 @@ void noInlayHintOnEmptyVersionWithSpacesTag() throws Exception { when(projectProvider.getProject(SpringProjectUtil.SPRING_BOOT)).thenReturn(resolvedProject); MavenMetadataProvider mavenMetadataProvider = mock(MavenMetadataProvider.class); - PomInlayHintHandler inlayHandler = new PomInlayHintHandler(server, projectFinder, ProjectObserver.NULL, projectProvider, mavenMetadataProvider); + PomInlayHintHandler inlayHandler = new PomInlayHintHandler(server, projectFinder, ProjectObserver.NULL, projectProvider, mavenMetadataProvider, new BootJavaConfig(server)); List hints = inlayHandler.handle(doc, doc.toRange(0, doc.getLength()), mock(CancelChecker.class)); assertEquals(0, hints.size()); @@ -306,7 +306,7 @@ void noInlayHintOnVersionTagWithNonParseableValue() throws Exception { when(projectProvider.getProject(SpringProjectUtil.SPRING_BOOT)).thenReturn(resolvedProject); MavenMetadataProvider mavenMetadataProvider = mock(MavenMetadataProvider.class); - PomInlayHintHandler inlayHandler = new PomInlayHintHandler(server, projectFinder, ProjectObserver.NULL, projectProvider, mavenMetadataProvider); + PomInlayHintHandler inlayHandler = new PomInlayHintHandler(server, projectFinder, ProjectObserver.NULL, projectProvider, mavenMetadataProvider, new BootJavaConfig(server)); List hints = inlayHandler.handle(doc, doc.toRange(0, doc.getLength()), mock(CancelChecker.class)); assertEquals(0, hints.size()); @@ -339,7 +339,7 @@ void noInlayHintWhenBuildFileUriIsNull() throws Exception { SpringProjectsProvider projectProvider = mock(SpringProjectsProvider.class); MavenMetadataProvider mavenMetadataProvider = mock(MavenMetadataProvider.class); - PomInlayHintHandler inlayHandler = new PomInlayHintHandler(server, projectFinder, ProjectObserver.NULL, projectProvider, mavenMetadataProvider); + PomInlayHintHandler inlayHandler = new PomInlayHintHandler(server, projectFinder, ProjectObserver.NULL, projectProvider, mavenMetadataProvider, new BootJavaConfig(server)); List hints = inlayHandler.handle(doc, doc.toRange(0, doc.getLength()), mock(CancelChecker.class)); assertEquals(0, hints.size()); @@ -368,7 +368,7 @@ void noInlayHintWhenNotSpringBootProject() throws Exception { SpringProjectsProvider projectProvider = mock(SpringProjectsProvider.class); MavenMetadataProvider mavenMetadataProvider = mock(MavenMetadataProvider.class); - PomInlayHintHandler inlayHandler = new PomInlayHintHandler(server, projectFinder, ProjectObserver.NULL, projectProvider, mavenMetadataProvider); + PomInlayHintHandler inlayHandler = new PomInlayHintHandler(server, projectFinder, ProjectObserver.NULL, projectProvider, mavenMetadataProvider, new BootJavaConfig(server)); List hints = inlayHandler.handle(doc, doc.toRange(0, doc.getLength()), mock(CancelChecker.class)); assertEquals(0, hints.size()); @@ -403,7 +403,7 @@ void upgradePatchVersionInlay_AlreadyOnLatestPatch() throws Exception { when(projectProvider.getProject(SpringProjectUtil.SPRING_BOOT)).thenReturn(resolvedProject); MavenMetadataProvider mavenMetadataProvider = mock(MavenMetadataProvider.class); - PomInlayHintHandler inlayHandler = new PomInlayHintHandler(server, projectFinder, ProjectObserver.NULL, projectProvider, mavenMetadataProvider); + PomInlayHintHandler inlayHandler = new PomInlayHintHandler(server, projectFinder, ProjectObserver.NULL, projectProvider, mavenMetadataProvider, new BootJavaConfig(server)); List hints = inlayHandler.handle(doc, doc.toRange(0, doc.getLength()), mock(CancelChecker.class)); assertEquals(0, hints.size()); diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ProblemTypesToJson.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ProblemTypesToJson.java index bf77b25956..17983f3e4c 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ProblemTypesToJson.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ProblemTypesToJson.java @@ -129,7 +129,9 @@ public ProblemTypeData(ProblemType type) { this.description = type.getDescription(); this.defaultSeverity =type.getDefaultSeverity().name(); this.label = type.getLabel(); - this.parameters = type.getParameters().stream().map(ProblemTypeParameterData::new).collect(Collectors.toList()); + if (type.getParameters() != null) { + this.parameters = type.getParameters().stream().map(ProblemTypeParameterData::new).collect(Collectors.toList()); + } } public ProblemTypeData(String defaultSeverity) { @@ -179,6 +181,7 @@ public static class ProblemCategoryData implements Comparable parameters; private int order; private List problemTypes; @@ -188,6 +191,9 @@ public static class ProblemCategoryData implements Comparable getParameters() { + return parameters; + } + public List getProblemTypes() { return problemTypes; } @@ -310,8 +320,8 @@ private void validateParameters(ProblemTypeData metadata, ProblemType actual) { if (md == null) { md = Collections.emptyList(); } - assertEquals(actual.getParameters().size(), md.size(), "parameter count for " + actual.getCode()); - List ap = new ArrayList<>(actual.getParameters()); + List ap = actual.getParameters() == null ? Collections.emptyList() : new ArrayList<>(actual.getParameters()); + assertEquals(ap.size(), md.size(), "parameter count for " + actual.getCode()); ap.sort(Comparator.comparing(ProblemTypeParameter::getKey)); List mdSorted = new ArrayList<>(md); mdSorted.sort(Comparator.comparing(ProblemTypeParameterData::getKey)); @@ -416,6 +426,7 @@ public void updatePackageJson(File packageJsonFile, String propertyPrefix) throw props.add(propertyPrefix + "." + category.getId() + "." + data.code, schema); addParameterPropertySchemas(props, category, data); } + addCategoryParameterPropertySchemas(props, category); configProps.add("properties", props); allProps.add(configProps); } @@ -424,6 +435,31 @@ public void updatePackageJson(File packageJsonFile, String propertyPrefix) throw FileUtils.writeStringToFile(packageJsonFile, newContent); } + private static void addCategoryParameterPropertySchemas(JsonObject props, ProblemCategoryData category) { + if (category.getParameters() == null || category.getParameters().isEmpty()) { + return; + } + String base = PROBLEM_PARAMETERS_PROPERTY_PREFIX + "." + category.getId() + "."; + for (ProblemTypeParameterData param : category.getParameters()) { + String fullKey = base + param.getKey(); + JsonObject schema = new JsonObject(); + String t = param.getType(); + if ("integer".equals(t)) { + schema.addProperty("type", "integer"); + schema.addProperty("default", Integer.parseInt(param.getDefaultValue())); + schema.addProperty("minimum", 1); + } else if ("boolean".equals(t)) { + schema.addProperty("type", "boolean"); + schema.addProperty("default", Boolean.parseBoolean(param.getDefaultValue())); + } else { + schema.addProperty("type", "string"); + schema.addProperty("default", param.getDefaultValue()); + } + schema.addProperty("description", param.getDescription()); + props.add(fullKey, schema); + } + } + private static void addParameterPropertySchemas(JsonObject props, ProblemCategoryData category, ProblemTypeData data) { if (data.getParameters() == null || data.getParameters().isEmpty()) { return; diff --git a/vscode-extensions/vscode-spring-boot/package.json b/vscode-extensions/vscode-spring-boot/package.json index e77591bded..08d7fd5614 100644 --- a/vscode-extensions/vscode-spring-boot/package.json +++ b/vscode-extensions/vscode-spring-boot/package.json @@ -1407,6 +1407,11 @@ "HINT", "ERROR" ] + }, + "spring-boot.ls.problem-parameters.version-validation.use-project-build-file": { + "type": "boolean", + "default": true, + "description": "Use project build file for version validation" } } },