Categorize boot problems. Boot 3 reconciling.

This commit is contained in:
aboyko
2022-07-28 14:33:27 -04:00
parent a8af7945c4
commit b50db98ce0
42 changed files with 1834 additions and 872 deletions

View File

@@ -32,7 +32,8 @@ Require-Bundle: org.eclipse.jdt.launching;bundle-version="3.9.0",
com.google.gson,
org.eclipse.lsp4e.jdt;bundle-version="0.10.0",
org.springsource.ide.eclipse.commons.boot.ls,
org.springframework.ide.eclipse.editor.support
org.springframework.ide.eclipse.editor.support,
com.google.guava
Import-Package: com.google.common.base,
com.google.common.collect,
com.google.gson;version="2.7.0",

View File

@@ -132,24 +132,6 @@
name="Spring Boot Language Server">
</page>
<page
name="Properties Editor"
category="org.springframework.tooling.boot.ls.preferences"
class="org.springframework.tooling.boot.ls.prefs.ApplicationPropertiesEditorProblemSeverityPrefsPage"
id="org.springframework.tooling.boot.ls.prefs.ApplicationPropertiesEditorProblemSeverityPrefsPage">
</page>
<page
name="Yaml Editor"
category="org.springframework.tooling.boot.ls.preferences"
class="org.springframework.tooling.boot.ls.prefs.ApplicationYamlEditorProblemSeverityPrefsPage"
id="org.springframework.tooling.boot.ls.prefs.ApplicationYamlEditorProblemSeverityPrefsPage">
</page>
<page
name="Java Editor"
category="org.springframework.tooling.boot.ls.preferences"
class="org.springframework.tooling.boot.ls.prefs.SpringJavaProblemSeverityPrefsPage"
id="org.springframework.tooling.boot.ls.prefs.SpringJavaProblemSeverityPrefsPage">
</page>
</extension>

View File

@@ -21,6 +21,7 @@ import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.keys.IBindingService;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.framework.BundleContext;
import org.springframework.tooling.boot.ls.prefs.CategoryProblemsSeverityPrefsPage;
/**
* Boot-Java LS extension plugin
@@ -51,7 +52,11 @@ public class BootLanguageServerPlugin extends AbstractUIPlugin {
super.start(context);
deactivateDuplicateKeybindings();
BootJavaPreferencesPage.manageCodeMiningPreferences();
CategoryProblemsSeverityPrefsPage.loadProblemCategoriesIntoPreferences();
}
@Override
public void stop(BundleContext context) throws Exception {

View File

@@ -22,7 +22,6 @@ import java.util.Map;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.util.IPropertyChangeListener;
@@ -32,6 +31,9 @@ import org.eclipse.lsp4j.InitializeResult;
import org.eclipse.lsp4j.jsonrpc.messages.Message;
import org.eclipse.lsp4j.jsonrpc.messages.ResponseMessage;
import org.eclipse.lsp4j.services.LanguageServer;
import org.springframework.tooling.boot.ls.prefs.CategoryProblemsSeverityPrefsPage;
import org.springframework.tooling.boot.ls.prefs.ProblemCategoryData;
import org.springframework.tooling.boot.ls.prefs.ProblemCategoryData.CategoryToggleData;
import org.springframework.tooling.ls.eclipse.commons.LanguageServerCommonsActivator;
import org.springsource.ide.eclipse.commons.boot.ls.remoteapps.RemoteBootAppsDataHolder;
import org.springsource.ide.eclipse.commons.boot.ls.remoteapps.RemoteBootAppsDataHolder.RemoteAppData;
@@ -194,6 +196,7 @@ public class DelegatingStreamConnectionProvider implements StreamConnectionProvi
settings.put("boot-java", bootJavaObj);
putValidationPreferences(settings);
putValidationCategoryToggles(settings);
this.languageServer.getWorkspaceService().didChangeConfiguration(new DidChangeConfigurationParams(settings));
}
@@ -213,6 +216,23 @@ public class DelegatingStreamConnectionProvider implements StreamConnectionProvi
Log.log(e);
}
}
private void putValidationCategoryToggles(Map<String, Object> settings) {
try {
IEclipsePreferences prefs = BootLanguageServerPlugin.getPreferences();
for (ProblemCategoryData category : CategoryProblemsSeverityPrefsPage.ALL_PROBLEM_CATEGORIES) {
if (category.getToggle() != null) {
CategoryToggleData toggle = category.getToggle();
String val = prefs.get(toggle.getPreferenceKey(), null);
if (val != null) {
dotPut(settings, toggle.getPreferenceKey(), val);
}
}
}
} catch (Exception e) {
Log.log(e);
}
}
private void dotPut(Object _settings, String dottedProperty, Object value) {
if (_settings instanceof Map) {

View File

@@ -1,33 +0,0 @@
/*******************************************************************************
* Copyright (c) 2020 Pivotal, 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.tooling.boot.ls.prefs;
import java.io.IOException;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.springframework.ide.eclipse.editor.support.preferences.ProblemSeverityPreferencesUtil;
import org.springframework.ide.eclipse.editor.support.preferences.ProblemSeverityPreferityPageFromMetadata;
import org.springframework.tooling.boot.ls.BootLanguageServerPlugin;
public class ApplicationPropertiesEditorProblemSeverityPrefsPage extends ProblemSeverityPreferityPageFromMetadata {
public static final ProblemSeverityPreferencesUtil util = new ProblemSeverityPreferencesUtil("problem.properties.");
public ApplicationPropertiesEditorProblemSeverityPrefsPage() throws IOException {
super(util, LanguageServerProblemTypesMetadata.load().get("application-properties"));
}
@Override
protected String getPluginId() {
return BootLanguageServerPlugin.PLUGIN_ID;
}
}

View File

@@ -1,31 +0,0 @@
/*******************************************************************************
* Copyright (c) 2020 Pivotal, 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.tooling.boot.ls.prefs;
import java.io.IOException;
import org.springframework.ide.eclipse.editor.support.preferences.ProblemSeverityPreferencesUtil;
import org.springframework.ide.eclipse.editor.support.preferences.ProblemSeverityPreferityPageFromMetadata;
import org.springframework.tooling.boot.ls.BootLanguageServerPlugin;
public class ApplicationYamlEditorProblemSeverityPrefsPage extends ProblemSeverityPreferityPageFromMetadata {
public static final ProblemSeverityPreferencesUtil util = new ProblemSeverityPreferencesUtil("problem.yaml.");
public ApplicationYamlEditorProblemSeverityPrefsPage() throws IOException {
super(util, LanguageServerProblemTypesMetadata.load().get("application-yaml"));
}
@Override
protected String getPluginId() {
return BootLanguageServerPlugin.PLUGIN_ID;
}
}

View File

@@ -0,0 +1,118 @@
/*******************************************************************************
* Copyright (c) 2022 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* VMware, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.tooling.boot.ls.prefs;
import java.util.Collections;
import java.util.List;
import org.eclipse.core.runtime.preferences.DefaultScope;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.jface.preference.ComboFieldEditor;
import org.eclipse.jface.preference.PreferenceManager;
import org.eclipse.jface.preference.PreferenceNode;
import org.eclipse.ui.PlatformUI;
import org.springframework.ide.eclipse.editor.support.preferences.ProblemSeverityPreferencesUtil;
import org.springframework.ide.eclipse.editor.support.preferences.ProblemSeverityPreferityPageFromMetadata;
import org.springframework.tooling.boot.ls.BootLanguageServerPlugin;
import org.springframework.tooling.boot.ls.prefs.ProblemCategoryData.CategoryToggleData;
import com.google.common.collect.ImmutableList;
public class CategoryProblemsSeverityPrefsPage extends ProblemSeverityPreferityPageFromMetadata {
public static ImmutableList<ProblemCategoryData> ALL_PROBLEM_CATEGORIES;
private static final String PREF_KEY_PREFIX = "";
private ProblemCategoryData category;
public CategoryProblemsSeverityPrefsPage(ProblemCategoryData category) {
super(new ProblemSeverityPreferencesUtil("problem." + category.getId() + "."), category.getProblemTypes());
this.category = category;
setTitle(category.getLabel());
}
@Override
protected String getPluginId() {
return BootLanguageServerPlugin.PLUGIN_ID;
}
@Override
protected void initializeDefaults() {
if (category.getToggle() != null) {
IEclipsePreferences defaults = DefaultScope.INSTANCE.getNode(getPluginId());
defaults.put(PREF_KEY_PREFIX + category.getToggle().getPreferenceKey(), category.getToggle().getDefaultValue());
}
super.initializeDefaults();
}
@Override
protected void createFieldEditors() {
if (category.getToggle() != null) {
CategoryToggleData toggle = category.getToggle();
ComboFieldEditor field = new ComboFieldEditor(
PREF_KEY_PREFIX + toggle.getPreferenceKey(),
toggle.getLabel(),
createToggleValues(toggle.getValues()),
getFieldEditorParent()
);
addField(field);
}
super.createFieldEditors();
}
private static String[][] createToggleValues(String[] values) {
String[][] res = new String[values.length][2];
for (int i = 0; i < values.length; i++) {
String value = values[i];
res[i][0] = value.substring(0, 1).toUpperCase() + value.substring(1).toLowerCase();
res[i][1] = value;
}
return res;
}
public static void loadProblemCategoriesIntoPreferences() throws Exception {
List<ProblemCategoryData> categories = LanguageServerProblemTypesMetadata.load();
Collections.sort(categories, (e1, e2) -> e1.getOrder() - e2.getOrder());
for (ProblemCategoryData categoryData : categories) {
//create a new PreferenceNode that will appear in the Preference window
PreferenceNode node = new PreferenceNode(BootLanguageServerPlugin.PLUGIN_ID + "." + categoryData.getId()) {
@Override
public void createPage() {
CategoryProblemsSeverityPrefsPage page = new CategoryProblemsSeverityPrefsPage(categoryData);
page.init(PlatformUI.getWorkbench());
page.setTitle(getLabelText());
setPage(page);
}
@Override
public String getLabelText() {
return categoryData.getLabel();
}
};
//use workbenches's preference manager
PreferenceManager pm= PlatformUI.getWorkbench().getPreferenceManager();
pm.addTo("org.eclipse.lsp4e.preferences/org.springframework.tooling.ls.eclipse.commons.console.preferences/org.springframework.tooling.boot.ls.preferences", node); //add the node in the PreferenceManager
}
ALL_PROBLEM_CATEGORIES = ImmutableList.copyOf(categories);
}
}

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2020 Pivotal, Inc.
* Copyright (c) 2020, 2022 Pivotal, 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
@@ -11,19 +11,35 @@
package org.springframework.tooling.boot.ls.prefs;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Map;
import java.io.Reader;
import java.util.List;
import org.eclipse.core.runtime.FileLocator;
import org.springframework.ide.eclipse.editor.support.preferences.ProblemSeverityPreferityPageFromMetadata.ProblemTypeData;
import org.springframework.tooling.boot.ls.BootLanguageServerPlugin;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
public class LanguageServerProblemTypesMetadata {
public static Map<String, ProblemTypeData[]> load() throws IOException {
public static List<ProblemCategoryData> load() throws IOException {
File root = FileLocator.getBundleFile(BootLanguageServerPlugin.getDefault().getBundle());
File metadataFile = root.toPath().resolve("servers/spring-boot-language-server/BOOT-INF/classes/problem-types.json").toFile();
return ApplicationPropertiesEditorProblemSeverityPrefsPage.readFromFile(metadataFile);
return readCategoriesFromFile(metadataFile);
}
public static List<ProblemCategoryData> readCategoriesFromFile(File metadataFile) throws FileNotFoundException, IOException {
Gson gson = new Gson();
TypeToken<List<ProblemCategoryData>> tt = new TypeToken<List<ProblemCategoryData>>() {
private static final long serialVersionUID = 1L;
};
try (Reader json = new FileReader(metadataFile)) {
return gson.fromJson(json, tt.getType());
}
}
}

View File

@@ -0,0 +1,76 @@
/*******************************************************************************
* Copyright (c) 2022 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* VMware, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.tooling.boot.ls.prefs;
import org.springframework.ide.eclipse.editor.support.preferences.ProblemSeverityPreferityPageFromMetadata.ProblemTypeData;
public class ProblemCategoryData {
private String id;
private String label;
private String description;
private CategoryToggleData toggle;
private int order;
private ProblemTypeData[] problemTypes;
ProblemCategoryData() {}
public String getLabel() {
return label;
}
public String getDescription() {
return description;
}
public CategoryToggleData getToggle() {
return toggle;
}
public int getOrder() {
return order;
}
public String getId() {
return id;
}
public ProblemTypeData[] getProblemTypes() {
return problemTypes;
}
public static class CategoryToggleData {
private String label;
private String[] values;
private String preferenceKey;
private String defaultValue;
public String getLabel() {
return label;
}
public String[] getValues() {
return values;
}
public String getPreferenceKey() {
return preferenceKey;
}
public String getDefaultValue() {
return defaultValue;
}
}
}

View File

@@ -1,32 +0,0 @@
/*******************************************************************************
* Copyright (c) 2020 Pivotal, 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.tooling.boot.ls.prefs;
import java.io.IOException;
import org.springframework.ide.eclipse.editor.support.preferences.ProblemSeverityPreferencesUtil;
import org.springframework.ide.eclipse.editor.support.preferences.ProblemSeverityPreferityPageFromMetadata;
import org.springframework.tooling.boot.ls.BootLanguageServerPlugin;
public class SpringJavaProblemSeverityPrefsPage extends ProblemSeverityPreferityPageFromMetadata {
public static final ProblemSeverityPreferencesUtil util = new ProblemSeverityPreferencesUtil("problem.java.");
public SpringJavaProblemSeverityPrefsPage() throws IOException {
super(util, LanguageServerProblemTypesMetadata.load().get("java"));
}
@Override
protected String getPluginId() {
return BootLanguageServerPlugin.PLUGIN_ID;
}
}

View File

@@ -130,7 +130,7 @@ public class BoshCloudConfigSchema extends SchemaSupport implements YamlSchema {
ASTTypeCache astTypes = models.astTypes;
for (YType defType : getDefinitionTypes()) {
toplevelType.require(Constraints.uniqueDefinition(astTypes, defType, YamlSchemaProblems.problemType("BOSH_CC_DUPLICATE_"+defType)));
toplevelType.require(Constraints.uniqueDefinition(astTypes, defType, YamlSchemaProblems.problemType("BOSH_CC_DUPLICATE_"+defType, BoshSchemaProblems.CATEGORY)));
}
}

View File

@@ -321,7 +321,7 @@ public class BoshDeploymentManifestSchema extends SchemaSupport implements YamlS
}
for (YType defType : getDefinitionTypes()) {
v2Schema.require(Constraints.uniqueDefinition(this.astTypes, defType, YamlSchemaProblems.problemType("BOSH_DUPLICATE_"+defType)));
v2Schema.require(Constraints.uniqueDefinition(this.astTypes, defType, YamlSchemaProblems.problemType("BOSH_DUPLICATE_"+defType, BoshSchemaProblems.CATEGORY)));
}
return v2Schema;
}

View File

@@ -10,12 +10,15 @@
*******************************************************************************/
package org.springframework.ide.vscode.bosh;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemCategory;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
import org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaProblems;
public class BoshSchemaProblems {
public static final ProblemCategory CATEGORY = new ProblemCategory("bosh", "Bosh YAML Validation", null);
public static final ProblemType MISSING_SHA1_PROPERTY = YamlSchemaProblems.problemType("MISSING_SHA1_PROPERTY", ProblemSeverity.WARNING);
public static final ProblemType MISSING_SHA1_PROPERTY = YamlSchemaProblems.problemType("MISSING_SHA1_PROPERTY", ProblemSeverity.WARNING, CATEGORY);
}

View File

@@ -46,6 +46,11 @@ public class BadWordReconcileEngine implements IReconcileEngine {
public String getLabel() {
return "Bad label";
}
@Override
public ProblemCategory getCategory() {
return null;
}
}
private final String[] BADWORDS = {

View File

@@ -0,0 +1,98 @@
/*******************************************************************************
* Copyright (c) 2022 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* VMware, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.languageserver.reconcile;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public final class ProblemCategory {
private static AtomicInteger counter = new AtomicInteger();
public static final ProblemCategory NO_CATEGORY = new ProblemCategory("uncategorized", "Uncategorized", null);
final private String id;
final private String label;
final private List<ProblemType> problemTypes = new ArrayList<>();;
final private Toggle toggle;
final public int order;
public ProblemCategory(String id, String label, Toggle toggle) {
this.id = id;
this.label = label;
this.toggle = toggle;
this.order = counter.getAndIncrement();
}
public String getId() {
return id;
}
public String getLabel() {
return label;
}
public List<ProblemType> getProblemTypes() {
return problemTypes;
}
public Toggle getToggle() {
return toggle;
}
public static final class Toggle {
public enum Option {
AUTO,
OFF,
ON
}
private final String label;
private final EnumSet<Option> values;
private final String preferenceKey;
private final Option defaultValue;
public Toggle(String label, EnumSet<Option> values, Option defaultValue, String preferenceKey) {
this.label = label;
this.values = values;
this.defaultValue = defaultValue;
this.preferenceKey = preferenceKey;
}
public String getLabel() {
return label;
}
public EnumSet<Option> getValues() {
return values;
}
public String getPreferenceKey() {
return preferenceKey;
}
public Option getDefaultValue() {
return defaultValue;
}
}
}

View File

@@ -28,4 +28,5 @@ public interface ProblemType {
String getCode();
String getLabel();
String getDescription();
ProblemCategory getCategory();
}

View File

@@ -10,10 +10,6 @@
*******************************************************************************/
package org.springframework.ide.vscode.commons.languageserver.reconcile;
import java.util.HashSet;
import org.springframework.ide.vscode.commons.util.Assert;
public class ProblemTypes {
/**
@@ -28,7 +24,7 @@ public class ProblemTypes {
* @return A newly create problem type.
*/
@Deprecated
public static ProblemType create(String typeName, ProblemSeverity defaultSeverity) {
public static ProblemType create(String typeName, ProblemSeverity defaultSeverity, ProblemCategory category) {
return new ProblemType() {
@Override
public String toString() {
@@ -50,6 +46,10 @@ public class ProblemTypes {
public String getLabel() {
return typeName;
}
@Override
public ProblemCategory getCategory() {
return category;
}
};
}

View File

@@ -0,0 +1,111 @@
/*******************************************************************************
* Copyright (c) 2022 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* VMware, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.rewrite.java;
import org.openrewrite.Cursor;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.search.UsesType;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.JavaType;
import org.openrewrite.java.tree.TypeTree;
import org.openrewrite.java.tree.TypeUtils;
public class PreciseBeanType extends Recipe {
private final static String BEAN = "org.springframework.context.annotation.Bean";
private final static String MSG_KEY = "returnType";
@Override
public String getDisplayName() {
return "Replace Bean method return types with concrete types being returned. This is required for Spring 6 AOT";
}
@Override
protected TreeVisitor<?, ExecutionContext> getSingleSourceApplicableTest() {
return new UsesType<ExecutionContext>(BEAN);
}
@Override
protected TreeVisitor<?, ExecutionContext> getVisitor() {
return new JavaIsoVisitor<ExecutionContext>() {
@Override
public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext executionContext) {
J.MethodDeclaration m = super.visitMethodDeclaration(method, executionContext);
if (isBeanMethod(m)) {
Object o = getCursor().pollMessage(MSG_KEY);
if (o != null) {
if (!o.equals(method.getReturnTypeExpression().getType())) {
if (o instanceof JavaType.FullyQualified) {
JavaType.FullyQualified actualType = (JavaType.FullyQualified) o;
if (m.getReturnTypeExpression() instanceof J.Identifier) {
J.Identifier identifierReturnExpr = (J.Identifier) m.getReturnTypeExpression();
maybeAddImport(actualType);
if (identifierReturnExpr.getType() instanceof JavaType.FullyQualified) {
maybeRemoveImport((JavaType.FullyQualified) identifierReturnExpr.getType());
}
m = m.withReturnTypeExpression(identifierReturnExpr
.withType(actualType)
.withSimpleName(actualType.getClassName())
);
} else if (m.getReturnTypeExpression() instanceof J.ParameterizedType) {
J.ParameterizedType parameterizedType = (J.ParameterizedType) m.getReturnTypeExpression();
maybeAddImport(actualType);
if (parameterizedType.getType() instanceof JavaType.FullyQualified) {
maybeRemoveImport((JavaType.FullyQualified) parameterizedType.getType());
}
m = m.withReturnTypeExpression(parameterizedType
.withType(actualType)
.withClazz(TypeTree.build(actualType.getClassName()).withType(actualType))
);
}
} else if (o instanceof JavaType.Array) {
JavaType.Array actualType = (JavaType.Array) o;
if (m.getReturnTypeExpression() instanceof J.ArrayType && actualType.getElemType() instanceof JavaType.FullyQualified) {
JavaType.FullyQualified actualElementType = (JavaType.FullyQualified) actualType.getElemType();
J.ArrayType arrayType = (J.ArrayType) m.getReturnTypeExpression();
maybeAddImport(actualElementType);
if (arrayType.getElementType() instanceof JavaType.FullyQualified) {
maybeRemoveImport((JavaType.FullyQualified) arrayType.getElementType());
}
m = m.withReturnTypeExpression(arrayType
.withElementType(TypeTree.build(actualElementType.getClassName()).withType(actualType))
);
}
}
}
}
}
return m;
}
private boolean isBeanMethod(J.MethodDeclaration m) {
return m.getLeadingAnnotations().stream().anyMatch(a -> TypeUtils.isOfClassType(a.getType(), BEAN));
}
@Override
public J.Return visitReturn(J.Return _return, ExecutionContext executionContext) {
Cursor methodCursor = getCursor();
while (methodCursor != null && !(methodCursor.getValue() instanceof J.Lambda || methodCursor.getValue() instanceof J.MethodDeclaration)) {
methodCursor = methodCursor.getParent();
}
if (methodCursor != null && methodCursor.getValue() instanceof J.MethodDeclaration) {
methodCursor.putMessage(MSG_KEY, _return.getExpression().getType());
}
return super.visitReturn(_return, executionContext);
}
};
}
}

View File

@@ -17,6 +17,7 @@ import java.util.stream.Stream;
import org.springframework.ide.vscode.commons.languageserver.quickfix.Quickfix.QuickfixData;
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixType;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemCategory;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemTypes;
@@ -46,25 +47,27 @@ import com.google.common.collect.ImmutableSet;
* @author Kris De Volder
*/
public class YamlSchemaProblems {
public static final ProblemCategory CATEGORY = new ProblemCategory("yaml-schema-problems", "YAML Shema Problems", null);
public static final ProblemType SYNTAX_PROBLEM = problemType("YamlSyntaxProblem");
public static final ProblemType SCHEMA_PROBLEM = problemType("YamlSchemaProblem");
public static final ProblemType DEPRECATED_PROPERTY = problemType("DeprecatedProperty", ProblemSeverity.WARNING);
public static final ProblemType DEPRECATED_VALUE = problemType("DeprecatedValue", ProblemSeverity.WARNING);
public static final ProblemType MISSING_PROPERTY = problemType("MissingProperty", ProblemSeverity.ERROR);
public static final ProblemType EXTRA_PROPERTY = problemType("ExtraProperty", ProblemSeverity.ERROR);
public static final ProblemType EMPTY_OPTIONAL_STRING = problemType("EmptyOptionalString", ProblemSeverity.WARNING);
public static final ProblemType SYNTAX_PROBLEM = problemType("YamlSyntaxProblem", CATEGORY);
public static final ProblemType SCHEMA_PROBLEM = problemType("YamlSchemaProblem", CATEGORY);
public static final ProblemType DEPRECATED_PROPERTY = problemType("DeprecatedProperty", ProblemSeverity.WARNING, CATEGORY);
public static final ProblemType DEPRECATED_VALUE = problemType("DeprecatedValue", ProblemSeverity.WARNING, CATEGORY);
public static final ProblemType MISSING_PROPERTY = problemType("MissingProperty", ProblemSeverity.ERROR, CATEGORY);
public static final ProblemType EXTRA_PROPERTY = problemType("ExtraProperty", ProblemSeverity.ERROR, CATEGORY);
public static final ProblemType EMPTY_OPTIONAL_STRING = problemType("EmptyOptionalString", ProblemSeverity.WARNING, CATEGORY);
public static final Set<ProblemType> PROPERTY_CONSTRAINT = ImmutableSet.of(
MISSING_PROPERTY, EXTRA_PROPERTY
);
public static ProblemType problemType(final String typeName, ProblemSeverity defaultSeverity) {
return ProblemTypes.create(typeName, defaultSeverity);
public static ProblemType problemType(final String typeName, ProblemSeverity defaultSeverity, ProblemCategory category) {
return ProblemTypes.create(typeName, defaultSeverity, category);
}
public static ProblemType problemType(final String typeName) {
return problemType(typeName, ProblemSeverity.ERROR);
public static ProblemType problemType(final String typeName, ProblemCategory category) {
return problemType(typeName, ProblemSeverity.ERROR, category);
}
public static ReconcileProblem syntaxProblem(String msg, int offset, int len) {

View File

@@ -10,12 +10,15 @@
*******************************************************************************/
package org.springframework.ide.vscode.concourse;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemCategory;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
import static org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaProblems.*;
public class PipelineYmlSchemaProblems {
public static final ProblemType UNUSED_RESOURCE = problemType("PipelineYamlUnusedResource", ProblemSeverity.ERROR);
public static final ProblemType UNKNOWN_GITHUB_ENTITY = problemType("UnknownGithubEntitity", ProblemSeverity.WARNING);
public static final ProblemCategory CATEGORY = new ProblemCategory("concourse", "Concource YAML Validation", null);
public static final ProblemType UNUSED_RESOURCE = problemType("PipelineYamlUnusedResource", ProblemSeverity.ERROR, CATEGORY);
public static final ProblemType UNKNOWN_GITHUB_ENTITY = problemType("UnknownGithubEntitity", ProblemSeverity.WARNING, CATEGORY);
}

View File

@@ -12,6 +12,7 @@ package org.springframework.ide.vscode.manifest.yaml;
import static org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaProblems.problemType;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemCategory;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
@@ -20,11 +21,12 @@ import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemTy
*/
public class ManifestYamlSchemaProblemsTypes {
public static final ProblemType UNKNOWN_SERVICES_PROBLEM = problemType("UnknownServicesProblem", ProblemSeverity.WARNING);
public static final ProblemType UNKNOWN_DOMAIN_PROBLEM = problemType("UnknownDomainProblem", ProblemSeverity.WARNING);
public static final ProblemType UNKNOWN_STACK_PROBLEM = problemType("UnknownStackProblem", ProblemSeverity.WARNING);
public static final ProblemType IGNORED_PROPERTY = problemType("IgnoredProperty", ProblemSeverity.WARNING);
public static final ProblemCategory CATEGORY = new ProblemCategory("manifest", "CF Manifest YAML Validation", null);
public static final ProblemType UNKNOWN_SERVICES_PROBLEM = problemType("UnknownServicesProblem", ProblemSeverity.WARNING, CATEGORY);
public static final ProblemType UNKNOWN_DOMAIN_PROBLEM = problemType("UnknownDomainProblem", ProblemSeverity.WARNING, CATEGORY);
public static final ProblemType UNKNOWN_STACK_PROBLEM = problemType("UnknownStackProblem", ProblemSeverity.WARNING, CATEGORY);
public static final ProblemType IGNORED_PROPERTY = problemType("IgnoredProperty", ProblemSeverity.WARNING, CATEGORY);
public static final ProblemType MUTUALLY_EXCLUSIVE_PROPERTY_PROBLEM = problemType("MutuallyExclusiveProperty",
ProblemSeverity.ERROR);
ProblemSeverity.ERROR, CATEGORY);
}

View File

@@ -15,7 +15,12 @@ import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.ide.vscode.boot.common.SpringProblemCategories;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemCategory.Toggle;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
import org.springframework.ide.vscode.commons.languageserver.util.ListenerList;
import org.springframework.ide.vscode.commons.languageserver.util.Settings;
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
@@ -30,6 +35,8 @@ import org.springframework.stereotype.Component;
@Component
public class BootJavaConfig implements InitializingBean {
private static final Logger log = LoggerFactory.getLogger(BootJavaConfig.class);
public static final boolean LIVE_INFORMATION_AUTOMATIC_TRACKING_ENABLED_DEFAULT = false;
public static final int LIVE_INFORMATION_AUTOMATIC_TRACKING_DELAY_DEFAULT = 5000;
@@ -110,8 +117,17 @@ public class BootJavaConfig implements InitializingBean {
}
public boolean isSpelExpressionValidationEnabled() {
Boolean enabled = settings.getBoolean("boot-java", "validation", "spel", "on");
return enabled != null ? enabled.booleanValue() : VALIDAITON_SPEL_EXPRESSIONS_ENABLED_DEFAULT;
Toggle categorySwitch = SpringProblemCategories.SPEL.getToggle();
String enabled = settings.getString(categorySwitch.getPreferenceKey().split("\\."));
if (enabled == null) {
return categorySwitch.getDefaultValue() == Toggle.Option.ON;
} else {
// Legacy case
if ("true".equalsIgnoreCase(enabled)) {
return true;
}
return Toggle.Option.valueOf(enabled) == Toggle.Option.ON;
}
}
public boolean areXmlHyperlinksEnabled() {
@@ -147,4 +163,18 @@ public class BootJavaConfig implements InitializingBean {
public Settings getRawSettings() {
return settings;
}
public Toggle.Option getProblemApplicability(ProblemType problem) {
try {
if (problem != null && problem.getCategory() != null && problem.getCategory().getToggle() != null) {
Toggle toggle = problem.getCategory().getToggle();
String s = settings.getString((toggle.getPreferenceKey()).split("\\."));
return s == null || s.isEmpty() ? toggle.getDefaultValue() : Toggle.Option.valueOf(s);
}
} catch (Exception e) {
log.error("", e);
}
return Toggle.Option.AUTO;
}
}

View File

@@ -0,0 +1,37 @@
/*******************************************************************************
* Copyright (c) 2022 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* VMware, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.common;
import java.util.EnumSet;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemCategory;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemCategory.Toggle;
import static org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemCategory.Toggle.Option.*;
public class SpringProblemCategories {
public static final ProblemCategory BOOT_2 = new ProblemCategory("boot2", "Boot 2.x Validation",
new Toggle("Enablement", EnumSet.allOf(Toggle.Option.class), AUTO, "boot-java.validation.java.boot2"));
public static final ProblemCategory BOOT_3 = new ProblemCategory("boot3", "Boot 3.x Validation",
new Toggle("Enablement", EnumSet.allOf(Toggle.Option.class), AUTO, "boot-java.validation.java.boot3"));
public static final ProblemCategory PROPERTIES = new ProblemCategory("application-properties", "Properties Validation", null);
public static final ProblemCategory YAML = new ProblemCategory("application-yaml", "YAML Properties Validation", null);
public static final ProblemCategory SPEL = new ProblemCategory("spel", "SPEL Validation",
new Toggle("Enablement", EnumSet.of(OFF, ON), ON, "boot-java.validation.spel.on"));
}

View File

@@ -1,29 +1,25 @@
/*******************************************************************************
* Copyright (c) 2020, 2022 Pivotal, Inc.
* Copyright (c) 2022 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
* VMware, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java;
import static org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity.HINT;
import static org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity.WARNING;
import org.springframework.ide.vscode.boot.common.SpringProblemCategories;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemCategory;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
import org.springframework.ide.vscode.commons.util.Assert;
import static org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity.*;
public enum Boot2JavaProblemType implements ProblemType {
/**
* This enum is supposed to represent *all* the different types of problems SpringBoot language server
* may detect in Java code.
*/
public enum SpringJavaProblemType implements ProblemType {
JAVA_SPEL_EXPRESSION_SYNTAX(ERROR, "SpEL parser raised a ParseException", "SpEL Expression Syntax"),
JAVA_AUTOWIRED_CONSTRUCTOR(WARNING, "Unnecessary `@Autowired` over the only constructor", "Unnecessary `@Autowired`"),
JAVA_PUBLIC_BEAN_METHOD(HINT, "Public modifier on `@Bean` method. They no longer have to be public visibility to be usable by Spring.", "public `@Bean` method"),
@@ -34,15 +30,14 @@ public enum SpringJavaProblemType implements ProblemType {
private String description;
private String label;
private SpringJavaProblemType(ProblemSeverity defaultSeverity, String description) {
private Boot2JavaProblemType(ProblemSeverity defaultSeverity, String description) {
this(defaultSeverity, description, null);
}
private SpringJavaProblemType(ProblemSeverity defaultSeverity, String description, String label) {
private Boot2JavaProblemType(ProblemSeverity defaultSeverity, String description, String label) {
this.description = description;
this.defaultSeverity = defaultSeverity;
this.label = label;
Assert.isLegal(name().startsWith("JAVA_"));
}
@Override
@@ -72,4 +67,9 @@ public enum SpringJavaProblemType implements ProblemType {
return name();
}
@Override
public ProblemCategory getCategory() {
return SpringProblemCategories.BOOT_2;
}
}

View File

@@ -0,0 +1,74 @@
/*******************************************************************************
* Copyright (c) 2020, 2022 Pivotal, 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java;
import static org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity.WARNING;
import org.springframework.ide.vscode.boot.common.SpringProblemCategories;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemCategory;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
/**
* This enum is supposed to represent *all* the different types of problems SpringBoot language server
* may detect in Java code.
*/
public enum Boot3JavaProblemType implements ProblemType {
JAVA_CONCRETE_BEAN_TYPE(WARNING, "Bean definition should have precise type for Spring 6 AOT", "Not precise bean defintion type");
private final ProblemSeverity defaultSeverity;
private String description;
private String label;
private Boot3JavaProblemType(ProblemSeverity defaultSeverity, String description) {
this(defaultSeverity, description, null);
}
private Boot3JavaProblemType(ProblemSeverity defaultSeverity, String description, String label) {
this.description = description;
this.defaultSeverity = defaultSeverity;
this.label = label;
}
@Override
public ProblemSeverity getDefaultSeverity() {
return defaultSeverity;
}
public String getLabel() {
if (label==null) {
label = createDefaultLabel();
}
return label;
}
@Override
public String getDescription() {
return description;
}
private String createDefaultLabel() {
String label = this.toString().substring(5).toLowerCase().replace('_', ' ');
return Character.toUpperCase(label.charAt(0)) + label.substring(1);
}
@Override
public String getCode() {
return name();
}
@Override
public ProblemCategory getCategory() {
return SpringProblemCategories.BOOT_3;
}
}

View File

@@ -0,0 +1,69 @@
/*******************************************************************************
* Copyright (c) 2022 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* VMware, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java;
import static org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity.ERROR;
import org.springframework.ide.vscode.boot.common.SpringProblemCategories;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemCategory;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
public enum SpelProblemType implements ProblemType {
JAVA_SPEL_EXPRESSION_SYNTAX(ERROR, "SpEL parser raised a ParseException", "SpEL Expression Syntax");
private final ProblemSeverity defaultSeverity;
private String description;
private String label;
private SpelProblemType(ProblemSeverity defaultSeverity, String description) {
this(defaultSeverity, description, null);
}
private SpelProblemType(ProblemSeverity defaultSeverity, String description, String label) {
this.description = description;
this.defaultSeverity = defaultSeverity;
this.label = label;
}
@Override
public ProblemSeverity getDefaultSeverity() {
return defaultSeverity;
}
public String getLabel() {
if (label==null) {
label = createDefaultLabel();
}
return label;
}
@Override
public String getDescription() {
return description;
}
private String createDefaultLabel() {
String label = this.toString().substring(5).toLowerCase().replace('_', ' ');
return Character.toUpperCase(label.charAt(0)) + label.substring(1);
}
@Override
public String getCode() {
return name();
}
@Override
public ProblemCategory getCategory() {
return SpringProblemCategories.SPEL;
}
}

View File

@@ -12,10 +12,8 @@ package org.springframework.ide.vscode.boot.java.handlers;
import org.springframework.expression.ParseException;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.ide.vscode.boot.java.SpringJavaProblemType;
import org.springframework.ide.vscode.boot.java.SpelProblemType;
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemTypes;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblem;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl;
import org.springframework.util.SystemPropertyUtils;
@@ -58,7 +56,7 @@ public class SpelExpressionReconciler implements Reconciler {
private void createProblem(String spelExpression, String message, int startPosition, int position, IProblemCollector problemCollector) {
int start = startPosition + position;
int length = spelExpression.length() - position;
ReconcileProblem problem = new ReconcileProblemImpl(SpringJavaProblemType.JAVA_SPEL_EXPRESSION_SYNTAX, message, start, length);
ReconcileProblem problem = new ReconcileProblemImpl(SpelProblemType.JAVA_SPEL_EXPRESSION_SYNTAX, message, start, length);
problemCollector.accept(problem);
}

View File

@@ -20,7 +20,7 @@ import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.springframework.ide.vscode.boot.java.Annotations;
import org.springframework.ide.vscode.boot.java.SpringJavaProblemType;
import org.springframework.ide.vscode.boot.java.Boot2JavaProblemType;
import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchies;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.java.SpringProjectUtil;
@@ -48,7 +48,7 @@ public class AutowiredConstructorReconciler implements AnnotationReconciler {
if (version.getMajor() >= 2) {
getSingleAutowiredConstructorDeclaringType(node, typeBinding).ifPresent(type -> {
ReconcileProblemImpl problem = new ReconcileProblemImpl(SpringJavaProblemType.JAVA_AUTOWIRED_CONSTRUCTOR, "Unnecesary @Autowired", node.getStartPosition(), node.getLength());
ReconcileProblemImpl problem = new ReconcileProblemImpl(Boot2JavaProblemType.JAVA_AUTOWIRED_CONSTRUCTOR, "Unnecesary @Autowired", node.getStartPosition(), node.getLength());
QuickfixType quickfixType = quickfixRegistry.getQuickfixType(AutowiredConstructorReconciler.REMOVE_UNNECESSARY_AUTOWIRED_FROM_CONSTRUCTOR);
if (quickfixType != null) {
problem.addQuickfix(new QuickfixData<>(

View File

@@ -23,7 +23,7 @@ import org.eclipse.jdt.core.dom.Modifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.java.Annotations;
import org.springframework.ide.vscode.boot.java.SpringJavaProblemType;
import org.springframework.ide.vscode.boot.java.Boot2JavaProblemType;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.java.SpringProjectUtil;
import org.springframework.ide.vscode.commons.java.Version;
@@ -63,10 +63,10 @@ public class BeanMethodNotPublicReconciler implements AnnotationReconciler {
.filter(modifier -> modifier.isPublic())
.findFirst()
.map(modifier -> new ReconcileProblemImpl(
SpringJavaProblemType.JAVA_PUBLIC_BEAN_METHOD, "public @Bean method",
Boot2JavaProblemType.JAVA_PUBLIC_BEAN_METHOD, "public @Bean method",
modifier.getStartPosition(), modifier.getLength()))
.orElse(new ReconcileProblemImpl(
SpringJavaProblemType.JAVA_PUBLIC_BEAN_METHOD, "public @Bean method",
Boot2JavaProblemType.JAVA_PUBLIC_BEAN_METHOD, "public @Bean method",
m.getName().getStartPosition(), m.getName().getLength()));
QuickfixType quickfixType = quickfixRegistry.getQuickfixType(REMOVE_PUBLIC_FROM_BEAN_METHOD);

View File

@@ -53,6 +53,7 @@ import org.springframework.ide.vscode.boot.java.rewrite.codeaction.BeanMethodsNo
import org.springframework.ide.vscode.boot.java.rewrite.codeaction.NoRequestMappingAnnotationCodeAction;
import org.springframework.ide.vscode.boot.java.rewrite.codeaction.UnnecessarySpringExtensionCodeAction;
import org.springframework.ide.vscode.boot.java.rewrite.reconcile.BeanMethodNotPublicProblem;
import org.springframework.ide.vscode.boot.java.rewrite.reconcile.PreciseBeanTypeProblem;
import org.springframework.ide.vscode.boot.java.rewrite.reconcile.NoAutowiredOnConstructorProblem;
import org.springframework.ide.vscode.boot.java.rewrite.reconcile.RecipeSpringJavaProblemDescriptor;
import org.springframework.ide.vscode.boot.java.rewrite.reconcile.UnnecessarySpringExtensionProblem;
@@ -113,7 +114,8 @@ public class RewriteRecipeRepository {
private List<RecipeSpringJavaProblemDescriptor> javaProblemDescriptors = List.of(
new BeanMethodNotPublicProblem(),
new NoAutowiredOnConstructorProblem(),
new UnnecessarySpringExtensionProblem()
new UnnecessarySpringExtensionProblem(),
new PreciseBeanTypeProblem()
);
public RewriteRecipeRepository(SimpleLanguageServer server, JavaProjectFinder projectFinder) {

View File

@@ -19,7 +19,6 @@ import java.util.stream.Collectors;
import org.openrewrite.ExecutionContext;
import org.openrewrite.InMemoryExecutionContext;
import org.openrewrite.Tree;
import org.openrewrite.internal.lang.Nullable;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.J.CompilationUnit;
@@ -27,15 +26,16 @@ import org.openrewrite.marker.Range;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.app.BootJavaConfig;
import org.springframework.ide.vscode.boot.java.SpringJavaProblemType;
import org.springframework.ide.vscode.boot.java.reconcilers.JavaReconciler;
import org.springframework.ide.vscode.boot.java.rewrite.RewriteRefactorings.Data;
import org.springframework.ide.vscode.boot.java.rewrite.reconcile.RecipeSpringJavaProblemDescriptor;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.java.SpringProjectUtil;
import org.springframework.ide.vscode.commons.languageserver.quickfix.Quickfix.QuickfixData;
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixRegistry;
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixType;
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblem;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl;
import org.springframework.ide.vscode.commons.rewrite.java.FixAssistMarker;
@@ -52,20 +52,20 @@ public class RewriteReconciler implements JavaReconciler {
private RewriteRecipeRepository recipeRepo;
private BootJavaConfig config;
public RewriteReconciler(RewriteRecipeRepository recipeRepo, RewriteCompilationUnitCache cuCache, QuickfixRegistry quickfixRegistry, BootJavaConfig config) {
this.recipeRepo = recipeRepo;
this.cuCache = cuCache;
this.quickfixRegistry = quickfixRegistry;
this.config = config;
}
@Override
public void reconcile(IJavaProject project, IDocument doc, IProblemCollector problemCollector) {
if (!config.isRewriteReconcileEnabled()) {
return;
}
try {
problemCollector.beginCollecting();
@@ -73,7 +73,17 @@ public class RewriteReconciler implements JavaReconciler {
recipeRepo.loaded.get();
List<RecipeSpringJavaProblemDescriptor> descriptors = recipeRepo.getProblemRecipeDescriptors().stream()
.filter(d -> d.isApplicable(project))
.filter(d -> d.getProblemType() != null)
.filter(d -> {
switch (config.getProblemApplicability(d.getProblemType())) {
case ON:
return SpringProjectUtil.isBootProject(project);
case OFF:
return false;
default: // AUTO
return d.isApplicable(project);
}
})
.collect(Collectors.toList());
if (!descriptors.isEmpty()) {
@@ -86,7 +96,7 @@ public class RewriteReconciler implements JavaReconciler {
new JavaIsoVisitor<ExecutionContext>() {
@Override
public @Nullable J visit(@Nullable Tree tree, ExecutionContext context) {
public J visit(Tree tree, ExecutionContext context) {
J t = super.visit(tree, context);
if (t instanceof J) {
List<FixAssistMarker> markers = t.getMarkers().findAll(FixAssistMarker.class);
@@ -125,7 +135,7 @@ public class RewriteReconciler implements JavaReconciler {
private ReconcileProblemImpl createProblemFromScope(IDocument doc, RecipeSpringJavaProblemDescriptor recipeFixDescriptor, RecipeScope s,
FixAssistMarker m, Range range) {
SpringJavaProblemType problemType = recipeFixDescriptor.getProblemType();
ProblemType problemType = recipeFixDescriptor.getProblemType();
ReconcileProblemImpl problem = new ReconcileProblemImpl(problemType, problemType.getLabel(), range.getStart().getOffset(), range.getEnd().getOffset() - range.getStart().getOffset());
QuickfixType quickfixType = quickfixRegistry.getQuickfixType(m.getRecipeId());
if (quickfixType != null) {

View File

@@ -10,7 +10,7 @@
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.rewrite.reconcile;
import org.springframework.ide.vscode.boot.java.SpringJavaProblemType;
import org.springframework.ide.vscode.boot.java.Boot2JavaProblemType;
import org.springframework.ide.vscode.boot.java.rewrite.RecipeScope;
import org.springframework.ide.vscode.boot.java.rewrite.codeaction.BeanMethodsNotPublicCodeAction;
@@ -22,8 +22,8 @@ public class BeanMethodNotPublicProblem extends BeanMethodsNotPublicCodeAction i
}
@Override
public SpringJavaProblemType getProblemType() {
return SpringJavaProblemType.JAVA_PUBLIC_BEAN_METHOD;
public Boot2JavaProblemType getProblemType() {
return Boot2JavaProblemType.JAVA_PUBLIC_BEAN_METHOD;
}
}

View File

@@ -18,13 +18,13 @@ import org.openrewrite.internal.ListUtils;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.JavaVisitor;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.Statement;
import org.openrewrite.java.tree.TypeUtils;
import org.openrewrite.java.tree.J.ClassDeclaration;
import org.openrewrite.java.tree.J.MethodDeclaration;
import org.openrewrite.java.tree.JavaType.FullyQualified;
import org.openrewrite.java.tree.Statement;
import org.openrewrite.java.tree.TypeUtils;
import org.springframework.ide.vscode.boot.java.Annotations;
import org.springframework.ide.vscode.boot.java.SpringJavaProblemType;
import org.springframework.ide.vscode.boot.java.Boot2JavaProblemType;
import org.springframework.ide.vscode.boot.java.rewrite.RecipeScope;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.rewrite.java.AnnotationHierarchies;
@@ -105,8 +105,8 @@ public class NoAutowiredOnConstructorProblem implements RecipeSpringJavaProblemD
}
@Override
public SpringJavaProblemType getProblemType() {
return SpringJavaProblemType.JAVA_AUTOWIRED_CONSTRUCTOR;
public Boot2JavaProblemType getProblemType() {
return Boot2JavaProblemType.JAVA_AUTOWIRED_CONSTRUCTOR;
}
private static boolean isConstructor(Statement s) {

View File

@@ -0,0 +1,98 @@
/*******************************************************************************
* Copyright (c) 2022 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
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* VMware, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.rewrite.reconcile;
import static org.springframework.ide.vscode.commons.java.SpringProjectUtil.springBootVersionGreaterOrEqual;
import org.openrewrite.Cursor;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Tree;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.JavaVisitor;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.J.Return;
import org.openrewrite.java.tree.JavaType;
import org.openrewrite.java.tree.TypeUtils;
import org.springframework.ide.vscode.boot.java.Boot3JavaProblemType;
import org.springframework.ide.vscode.boot.java.rewrite.RecipeCodeActionDescriptor;
import org.springframework.ide.vscode.boot.java.rewrite.RecipeScope;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.rewrite.java.FixAssistMarker;
public class PreciseBeanTypeProblem implements RecipeSpringJavaProblemDescriptor {
private static final String LABEL = "Ensure concrete bean type";
private static final String MSG_KEY = "returnType";
@Override
public String getRecipeId() {
return "org.springframework.ide.vscode.commons.rewrite.java.PreciseBeanType";
}
@Override
public String getLabel(RecipeScope s) {
return RecipeCodeActionDescriptor.buildLabel(LABEL, s);
}
@Override
public RecipeScope[] getScopes() {
return RecipeScope.values();
}
@Override
public JavaVisitor<ExecutionContext> getMarkerVisitor() {
return new JavaIsoVisitor<>() {
public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext executionContext) {
J.MethodDeclaration m = super.visitMethodDeclaration(method, executionContext);
if (m.getLeadingAnnotations().stream().anyMatch(a -> TypeUtils.isOfClassType(a.getType(), "org.springframework.context.annotation.Bean"))) {
Object o = getCursor().pollMessage(MSG_KEY);
if (o != null && !o.equals(m.getReturnTypeExpression().getType())) {
if ((o instanceof JavaType.FullyQualified && m.getReturnTypeExpression().getType() instanceof JavaType.FullyQualified)
|| (o instanceof JavaType.Array && m.getReturnTypeExpression().getType() instanceof JavaType.Array)) {
m = m.withReturnTypeExpression(m.getReturnTypeExpression().withMarkers(m.getReturnTypeExpression().getMarkers().add(
new FixAssistMarker(Tree.randomId()).withScope(m.getId()).withRecipeId(getRecipeId()))));
}
}
}
return m;
}
@Override
public Return visitReturn(Return _return, ExecutionContext executionContext) {
Cursor methodCursor = getCursor();
if (_return.getExpression() != null) {
while (methodCursor != null && !(methodCursor.getValue() instanceof J.Lambda || methodCursor.getValue() instanceof J.MethodDeclaration)) {
methodCursor = methodCursor.getParent();
}
if (methodCursor != null && methodCursor.getValue() instanceof J.MethodDeclaration) {
methodCursor.putMessage(MSG_KEY, _return.getExpression().getType());
}
}
return super.visitReturn(_return, executionContext);
}
};
}
@Override
public boolean isApplicable(IJavaProject project) {
return springBootVersionGreaterOrEqual(3, 0, 0).test(project);
}
@Override
public Boot3JavaProblemType getProblemType() {
return Boot3JavaProblemType.JAVA_CONCRETE_BEAN_TYPE;
}
}

View File

@@ -10,11 +10,11 @@
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.rewrite.reconcile;
import org.springframework.ide.vscode.boot.java.SpringJavaProblemType;
import org.springframework.ide.vscode.boot.java.rewrite.RecipeCodeActionDescriptor;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
public interface RecipeSpringJavaProblemDescriptor extends RecipeCodeActionDescriptor {
SpringJavaProblemType getProblemType();
ProblemType getProblemType();
}

View File

@@ -10,7 +10,7 @@
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.rewrite.reconcile;
import org.springframework.ide.vscode.boot.java.SpringJavaProblemType;
import org.springframework.ide.vscode.boot.java.Boot2JavaProblemType;
import org.springframework.ide.vscode.boot.java.rewrite.RecipeScope;
import org.springframework.ide.vscode.boot.java.rewrite.codeaction.UnnecessarySpringExtensionCodeAction;
@@ -23,8 +23,8 @@ public class UnnecessarySpringExtensionProblem extends UnnecessarySpringExtensio
}
@Override
public SpringJavaProblemType getProblemType() {
return SpringJavaProblemType.JAVA_TEST_SPRING_EXTENSION;
public Boot2JavaProblemType getProblemType() {
return Boot2JavaProblemType.JAVA_TEST_SPRING_EXTENSION;
}
}

View File

@@ -13,7 +13,8 @@ package org.springframework.ide.vscode.boot.properties.reconcile;
import static org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity.ERROR;
import static org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity.WARNING;
import org.springframework.boot.SpringApplication;
import org.springframework.ide.vscode.boot.common.SpringProblemCategories;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemCategory;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
@@ -77,4 +78,9 @@ public enum ApplicationPropertiesProblemType implements ProblemType {
return name();
}
@Override
public ProblemCategory getCategory() {
return SpringProblemCategories.PROPERTIES;
}
}

View File

@@ -13,6 +13,8 @@ package org.springframework.ide.vscode.boot.yaml.reconcile;
import static org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity.ERROR;
import static org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity.WARNING;
import org.springframework.ide.vscode.boot.common.SpringProblemCategories;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemCategory;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
@@ -80,4 +82,9 @@ public enum ApplicationYamlProblemType implements ProblemType {
return name();
}
@Override
public ProblemCategory getCategory() {
return SpringProblemCategories.YAML;
}
}

View File

@@ -1,176 +1,240 @@
{
"java": [
{
"code": "JAVA_SPEL_EXPRESSION_SYNTAX",
"label": "SpEL Expression Syntax",
"description": "SpEL parser raised a ParseException",
"defaultSeverity": "ERROR"
[
{
"id": "boot2",
"label": "Boot 2.x Validation",
"toggle": {
"label": "Enablement",
"values": [
"AUTO",
"OFF",
"ON"
],
"preferenceKey": "boot-java.validation.java.boot2",
"defaultValue": "AUTO"
},
{
"code": "JAVA_AUTOWIRED_CONSTRUCTOR",
"label": "Unnecessary `@Autowired`",
"description": "Unnecessary `@Autowired` over the only constructor",
"defaultSeverity": "WARNING"
"order": 1,
"problemTypes": [
{
"code": "JAVA_AUTOWIRED_CONSTRUCTOR",
"label": "Unnecessary `@Autowired`",
"description": "Unnecessary `@Autowired` over the only constructor",
"defaultSeverity": "WARNING"
},
{
"code": "JAVA_PUBLIC_BEAN_METHOD",
"label": "public `@Bean` method",
"description": "Public modifier on `@Bean` method. They no longer have to be public visibility to be usable by Spring.",
"defaultSeverity": "HINT"
},
{
"code": "JAVA_TEST_SPRING_EXTENSION",
"label": "Unnecessary `@SpringExtension`",
"description": "`@SpringBootTest` and all test slice annotations already applies `@SpringExtension` as of Spring Boot 2.1.0.",
"defaultSeverity": "WARNING"
}
]
},
{
"id": "boot3",
"label": "Boot 3.x Validation",
"toggle": {
"label": "Enablement",
"values": [
"AUTO",
"OFF",
"ON"
],
"preferenceKey": "boot-java.validation.java.boot3",
"defaultValue": "AUTO"
},
{
"code": "JAVA_PUBLIC_BEAN_METHOD",
"label": "public `@Bean` method",
"description": "Public modifier on `@Bean` method. They no longer have to be public visibility to be usable by Spring.",
"defaultSeverity": "HINT"
"order": 2,
"problemTypes": [
{
"code": "JAVA_CONCRETE_BEAN_TYPE",
"label": "Not precise bean defintion type",
"description": "Bean definition should have precise type for Spring 6 AOT",
"defaultSeverity": "WARNING"
}
]
},
{
"id": "application-properties",
"label": "Properties Validation",
"order": 3,
"problemTypes": [
{
"code": "PROP_INVALID_BEAN_NAVIGATION",
"label": "Invalid bean navigation",
"description": "Accessing a 'bean property' in a type that doesn't have properties (e.g. like String or Integer)",
"defaultSeverity": "ERROR"
},
{
"code": "PROP_INVALID_INDEXED_NAVIGATION",
"label": "Invalid indexed navigation",
"description": "Accessing a property using [] in a type that doesn't support that",
"defaultSeverity": "ERROR"
},
{
"code": "PROP_EXPECTED_DOT_OR_LBRACK",
"label": "Expected dot or lbrack",
"description": "Unexpected character found where a '.' or '[' was expected",
"defaultSeverity": "ERROR"
},
{
"code": "PROP_NO_MATCHING_RBRACK",
"label": "No matching rbrack",
"description": "Found a '[' but no matching ']'",
"defaultSeverity": "ERROR"
},
{
"code": "PROP_NON_INTEGER_IN_BRACKETS",
"label": "Non integer in brackets",
"description": "Use of [..] navigation with non-integer value",
"defaultSeverity": "ERROR"
},
{
"code": "PROP_VALUE_TYPE_MISMATCH",
"label": "Value type mismatch",
"description": "Expecting a value of a certain type, but value doesn't parse as such",
"defaultSeverity": "ERROR"
},
{
"code": "PROP_INVALID_BEAN_PROPERTY",
"label": "Invalid bean property",
"description": "Accessing a named property in a type that doesn't provide a property accessor with that name",
"defaultSeverity": "ERROR"
},
{
"code": "PROP_UNKNOWN_PROPERTY",
"label": "Unknown property",
"description": "Property-key not found in any configuration metadata on the project's classpath",
"defaultSeverity": "WARNING"
},
{
"code": "PROP_DEPRECATED",
"label": "Deprecated",
"description": "Property is marked as Deprecated",
"defaultSeverity": "WARNING"
},
{
"code": "PROP_DUPLICATE_KEY",
"label": "Duplicate key",
"description": "Multiple assignments to the same property value",
"defaultSeverity": "ERROR"
},
{
"code": "PROP_SYNTAX_ERROR",
"label": "Syntax error",
"description": "Syntax Error",
"defaultSeverity": "ERROR"
}
]
},
{
"id": "application-yaml",
"label": "YAML Properties Validation",
"order": 4,
"problemTypes": [
{
"code": "YAML_SYNTAX_ERROR",
"label": "Syntax error",
"description": "Error parsing the input using snakeyaml",
"defaultSeverity": "ERROR"
},
{
"code": "YAML_UNKNOWN_PROPERTY",
"label": "Unknown property",
"description": "Property-key not found in the configuration metadata on the project's classpath",
"defaultSeverity": "WARNING"
},
{
"code": "YAML_VALUE_TYPE_MISMATCH",
"label": "Value type mismatch",
"description": "Expecting a value of a certain type, but value doesn't parse as such",
"defaultSeverity": "ERROR"
},
{
"code": "YAML_EXPECT_SCALAR",
"label": "Expect scalar",
"description": "Expecting a 'scalar' value but found something more complex.",
"defaultSeverity": "ERROR"
},
{
"code": "YAML_EXPECT_TYPE_FOUND_SEQUENCE",
"label": "Expect type found sequence",
"description": "Found a 'sequence' node where a non 'list-like' type is expected",
"defaultSeverity": "ERROR"
},
{
"code": "YAML_EXPECT_TYPE_FOUND_MAPPING",
"label": "Expect type found mapping",
"description": "Found a 'mapping' node where a type that can't be treated as a 'property map' is expected",
"defaultSeverity": "ERROR"
},
{
"code": "YAML_EXPECT_MAPPING",
"label": "Expect mapping",
"description": "Expecting a 'mapping' node but found something else",
"defaultSeverity": "ERROR"
},
{
"code": "YAML_EXPECT_BEAN_PROPERTY_NAME",
"label": "Expect bean property name",
"description": "Expecting a 'bean property' name but found something more complex",
"defaultSeverity": "ERROR"
},
{
"code": "YAML_INVALID_BEAN_PROPERTY",
"label": "Invalid bean property",
"description": "Accessing a named property in a type that doesn't provide a property accessor with that name",
"defaultSeverity": "ERROR"
},
{
"code": "YAML_DEPRECATED_ERROR",
"label": "Deprecated error",
"description": "Property is marked as Deprecated(Error)",
"defaultSeverity": "ERROR"
},
{
"code": "YAML_DEPRECATED_WARNING",
"label": "Deprecated warning",
"description": "Property is marked as Deprecated(Warning)",
"defaultSeverity": "WARNING"
},
{
"code": "YAML_DUPLICATE_KEY",
"label": "Duplicate key",
"description": "A mapping node contains multiple entries for the same key",
"defaultSeverity": "ERROR"
},
{
"code": "YAML_SHOULD_ESCAPE",
"label": "Should escape",
"description": "This key contains special characters and should probably be escaped by surrounding it with '[]'",
"defaultSeverity": "WARNING"
}
]
},
{
"id": "spel",
"label": "SPEL Validation",
"toggle": {
"label": "Enablement",
"values": [
"OFF",
"ON"
],
"preferenceKey": "boot-java.validation.spel.on",
"defaultValue": "ON"
},
{
"code": "JAVA_TEST_SPRING_EXTENSION",
"label": "Unnecessary `@SpringExtension`",
"description": "`@SpringBootTest` and all test slice annotations already applies `@SpringExtension` as of Spring Boot 2.1.0.",
"defaultSeverity": "WARNING"
}
],
"application-yaml": [
{
"code": "YAML_SYNTAX_ERROR",
"label": "Syntax error",
"description": "Error parsing the input using snakeyaml",
"defaultSeverity": "ERROR"
},
{
"code": "YAML_UNKNOWN_PROPERTY",
"label": "Unknown property",
"description": "Property-key not found in the configuration metadata on the project\u0027s classpath",
"defaultSeverity": "WARNING"
},
{
"code": "YAML_VALUE_TYPE_MISMATCH",
"label": "Value type mismatch",
"description": "Expecting a value of a certain type, but value doesn\u0027t parse as such",
"defaultSeverity": "ERROR"
},
{
"code": "YAML_EXPECT_SCALAR",
"label": "Expect scalar",
"description": "Expecting a \u0027scalar\u0027 value but found something more complex.",
"defaultSeverity": "ERROR"
},
{
"code": "YAML_EXPECT_TYPE_FOUND_SEQUENCE",
"label": "Expect type found sequence",
"description": "Found a \u0027sequence\u0027 node where a non \u0027list-like\u0027 type is expected",
"defaultSeverity": "ERROR"
},
{
"code": "YAML_EXPECT_TYPE_FOUND_MAPPING",
"label": "Expect type found mapping",
"description": "Found a \u0027mapping\u0027 node where a type that can\u0027t be treated as a \u0027property map\u0027 is expected",
"defaultSeverity": "ERROR"
},
{
"code": "YAML_EXPECT_MAPPING",
"label": "Expect mapping",
"description": "Expecting a \u0027mapping\u0027 node but found something else",
"defaultSeverity": "ERROR"
},
{
"code": "YAML_EXPECT_BEAN_PROPERTY_NAME",
"label": "Expect bean property name",
"description": "Expecting a \u0027bean property\u0027 name but found something more complex",
"defaultSeverity": "ERROR"
},
{
"code": "YAML_INVALID_BEAN_PROPERTY",
"label": "Invalid bean property",
"description": "Accessing a named property in a type that doesn\u0027t provide a property accessor with that name",
"defaultSeverity": "ERROR"
},
{
"code": "YAML_DEPRECATED_ERROR",
"label": "Deprecated error",
"description": "Property is marked as Deprecated(Error)",
"defaultSeverity": "ERROR"
},
{
"code": "YAML_DEPRECATED_WARNING",
"label": "Deprecated warning",
"description": "Property is marked as Deprecated(Warning)",
"defaultSeverity": "WARNING"
},
{
"code": "YAML_DUPLICATE_KEY",
"label": "Duplicate key",
"description": "A mapping node contains multiple entries for the same key",
"defaultSeverity": "ERROR"
},
{
"code": "YAML_SHOULD_ESCAPE",
"label": "Should escape",
"description": "This key contains special characters and should probably be escaped by surrounding it with \u0027[]\u0027",
"defaultSeverity": "WARNING"
}
],
"application-properties": [
{
"code": "PROP_INVALID_BEAN_NAVIGATION",
"label": "Invalid bean navigation",
"description": "Accessing a \u0027bean property\u0027 in a type that doesn\u0027t have properties (e.g. like String or Integer)",
"defaultSeverity": "ERROR"
},
{
"code": "PROP_INVALID_INDEXED_NAVIGATION",
"label": "Invalid indexed navigation",
"description": "Accessing a property using [] in a type that doesn\u0027t support that",
"defaultSeverity": "ERROR"
},
{
"code": "PROP_EXPECTED_DOT_OR_LBRACK",
"label": "Expected dot or lbrack",
"description": "Unexpected character found where a \u0027.\u0027 or \u0027[\u0027 was expected",
"defaultSeverity": "ERROR"
},
{
"code": "PROP_NO_MATCHING_RBRACK",
"label": "No matching rbrack",
"description": "Found a \u0027[\u0027 but no matching \u0027]\u0027",
"defaultSeverity": "ERROR"
},
{
"code": "PROP_NON_INTEGER_IN_BRACKETS",
"label": "Non integer in brackets",
"description": "Use of [..] navigation with non-integer value",
"defaultSeverity": "ERROR"
},
{
"code": "PROP_VALUE_TYPE_MISMATCH",
"label": "Value type mismatch",
"description": "Expecting a value of a certain type, but value doesn\u0027t parse as such",
"defaultSeverity": "ERROR"
},
{
"code": "PROP_INVALID_BEAN_PROPERTY",
"label": "Invalid bean property",
"description": "Accessing a named property in a type that doesn\u0027t provide a property accessor with that name",
"defaultSeverity": "ERROR"
},
{
"code": "PROP_UNKNOWN_PROPERTY",
"label": "Unknown property",
"description": "Property-key not found in any configuration metadata on the project\u0027s classpath",
"defaultSeverity": "WARNING"
},
{
"code": "PROP_DEPRECATED",
"label": "Deprecated",
"description": "Property is marked as Deprecated",
"defaultSeverity": "WARNING"
},
{
"code": "PROP_DUPLICATE_KEY",
"label": "Duplicate key",
"description": "Multiple assignments to the same property value",
"defaultSeverity": "ERROR"
},
{
"code": "PROP_SYNTAX_ERROR",
"label": "Syntax error",
"description": "Syntax Error",
"defaultSeverity": "ERROR"
}
]
}
"order": 5,
"problemTypes": [
{
"code": "JAVA_SPEL_EXPRESSION_SYNTAX",
"label": "SpEL Expression Syntax",
"description": "SpEL parser raised a ParseException",
"defaultSeverity": "ERROR"
}
]
}
]

View File

@@ -11,7 +11,9 @@
package org.springframework.ide.vscode.boot.test;
import org.junit.jupiter.api.Test;
import org.springframework.ide.vscode.boot.java.SpringJavaProblemType;
import org.springframework.ide.vscode.boot.java.Boot2JavaProblemType;
import org.springframework.ide.vscode.boot.java.Boot3JavaProblemType;
import org.springframework.ide.vscode.boot.java.SpelProblemType;
import org.springframework.ide.vscode.boot.properties.reconcile.ApplicationPropertiesProblemType;
import org.springframework.ide.vscode.boot.yaml.reconcile.ApplicationYamlProblemType;
@@ -25,7 +27,9 @@ public class ProblemTypesMetadataTest {
ProblemTypesToJson reader = new ProblemTypesToJson().read();
reader.validate("application-properties", ApplicationPropertiesProblemType.values());
reader.validate("application-yaml", ApplicationYamlProblemType.values());
reader.validate("java", SpringJavaProblemType.values());
reader.validate("boot2", Boot2JavaProblemType.values());
reader.validate("boot3", Boot3JavaProblemType.values());
reader.validate("spel", SpelProblemType.values());
}
}

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2020 Pivotal, Inc.
* Copyright (c) 2020, 2022 Pivotal, 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
@@ -17,19 +17,26 @@ import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.io.FileUtils;
import org.springframework.ide.vscode.boot.java.SpringJavaProblemType;
import org.springframework.ide.vscode.boot.java.Boot2JavaProblemType;
import org.springframework.ide.vscode.boot.java.Boot3JavaProblemType;
import org.springframework.ide.vscode.boot.java.SpelProblemType;
import org.springframework.ide.vscode.boot.properties.reconcile.ApplicationPropertiesProblemType;
import org.springframework.ide.vscode.boot.yaml.reconcile.ApplicationYamlProblemType;
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.ProblemSeverity;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
import org.springframework.ide.vscode.commons.util.Streams;
import org.springframework.ide.vscode.commons.yaml.path.YamlPath;
import org.springframework.ide.vscode.commons.yaml.util.JSONCursor;
@@ -58,6 +65,7 @@ public class ProblemTypesToJson {
public static final String packageJsonPath = "../../vscode-extensions/vscode-spring-boot/package.json";
public static final String resourcesPath = "src/main/resources";
public static final String metaDataFileName = "problem-types.json";
public static final String problemCategoriesFileName = "problem-categories.json";
public static class ProblemTypeData {
String code;
@@ -70,7 +78,7 @@ public class ProblemTypesToJson {
public ProblemTypeData(ProblemType type) {
this.code = type.getCode();
this.description = type.getDescription();
this.defaultSeverity =type.getDefaultSeverity().toString();
this.defaultSeverity =type.getDefaultSeverity().name();
this.label = type.getLabel();
}
@@ -108,46 +116,106 @@ public class ProblemTypesToJson {
this.label = label;
}
}
public static class ProblemCategoryData implements Comparable<ProblemCategoryData> {
private String id;
private String label;
private Toggle toggle;
private int order;
private List<ProblemTypeData> problemTypes;
ProblemCategoryData(ProblemCategory category, Collection<ProblemType> problemTypes) {
this.id = category.getId();
this.label = category.getLabel();
this.toggle = category.getToggle();
this.order = category.order;
this.problemTypes = problemTypes.stream().map(ProblemTypeData::new).collect(Collectors.toList());
}
ProblemCategoryData(ProblemCategory category) {
this(category, Collections.emptyList());
}
Map<String, ProblemTypeData[]> problemTypes = new HashMap<>();
public String getLabel() {
return label;
}
public Toggle getToggle() {
return toggle;
}
public int getOrder() {
return order;
}
public String getId() {
return id;
}
public List<ProblemTypeData> getProblemTypes() {
return problemTypes;
}
@Override
public int compareTo(ProblemCategoryData o) {
return getOrder() - o.getOrder();
}
}
private List<ProblemCategoryData> problemCategories = new ArrayList<>();
public static void main(String[] args) throws Exception {
ProblemTypesToJson writer = new ProblemTypesToJson();
writer.problemsFor("application-yaml", ApplicationYamlProblemType.values());
writer.problemsFor("application-properties", ApplicationPropertiesProblemType.values());
writer.problemsFor("java", SpringJavaProblemType.values());
writer.dump();
writer.collectProblemTypeData(SpelProblemType.values());
writer.collectProblemTypeData(ApplicationYamlProblemType.values());
writer.collectProblemTypeData(Boot2JavaProblemType.values());
writer.collectProblemTypeData(ApplicationPropertiesProblemType.values());
writer.collectProblemTypeData(Boot3JavaProblemType.values());
Collections.sort(writer.problemCategories);
writer.dump();
writer.updatePackageJson(new File(packageJsonPath), "spring-boot.ls.problem");
}
private void dump() throws IOException {
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String json = gson.toJson(problemTypes);
FileUtils.writeStringToFile(new File(resourcesPath+"/"+metaDataFileName), json);
Gson gson = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
String json = gson.toJson(problemCategories);
FileUtils.writeStringToFile(new File(resourcesPath+"/"+metaDataFileName), json);
}
private void problemsFor(String string, ProblemType[] values) {
ProblemTypeData[] data = new ProblemTypeData[values.length];
for (int i = 0; i < data.length; i++) {
data[i] = new ProblemTypeData(values[i]);
private void collectProblemTypeData(ProblemType[] problems) {
for (int i = 0; i < problems.length; i++) {
ProblemCategory category = problems[i].getCategory();
if (category != null) {
ProblemCategoryData categoryData = problemCategories.stream().filter(c -> c.getId().equals(category.getId())).findFirst().orElseGet(() -> {
ProblemCategoryData d = new ProblemCategoryData(category);
problemCategories.add(d);
return d;
});
categoryData.getProblemTypes().add(new ProblemTypeData(problems[i]));
} else {
throw new IllegalStateException("No category for problem " + problems[i].getCode());
}
}
problemTypes.put(string, data);
}
public ProblemTypesToJson read() throws IOException {
try (InputStream input = this.getClass().getResourceAsStream("/"+metaDataFileName)) {
Gson gson = new GsonBuilder().setPrettyPrinting().create();
TypeToken<Map<String, ProblemTypeData[]>> tt = new TypeToken<Map<String, ProblemTypeData[]>>() {};
problemTypes = gson.fromJson(new InputStreamReader(input), tt.getType());
TypeToken<List<ProblemCategoryData>> tt = new TypeToken<List<ProblemCategoryData>>() {};
problemCategories = gson.fromJson(new InputStreamReader(input), tt.getType());
}
return this;
}
public void validate(String name, ProblemType[] actualProblemTypes) {
ProblemTypeData[] metadataProblemTypes = problemTypes.get(name);
Set<String> metadataHas = Stream.of(metadataProblemTypes).map(x -> x.getCode()).collect(Collectors.toSet());
List<ProblemTypeData> metadataProblemTypes = problemCategories.stream().filter(c -> c.getId().equals(name)).findFirst().map(c -> c.getProblemTypes()).orElse(Collections.emptyList());
Set<String> metadataHas = metadataProblemTypes.stream().map(x -> x.getCode()).collect(Collectors.toSet());
Set<String> actualHas = Stream.of(actualProblemTypes).map(x-> x.getCode()).collect(Collectors.toSet());
for (String string : actualHas) {
if (!metadataHas.contains(string)) {
@@ -160,8 +228,8 @@ public class ProblemTypesToJson {
}
}
for (int i = 0; i < metadataProblemTypes.length; i++) {
ProblemTypeData metadata = metadataProblemTypes[i];
for (int i = 0; i < metadataProblemTypes.size(); i++) {
ProblemTypeData metadata = metadataProblemTypes.get(i);
ProblemType actual = actualProblemTypes[i];
assertEquals(actual.getCode(), metadata.getCode());
assertEquals(actual.getDescription(), metadata.getDescription());
@@ -170,33 +238,99 @@ public class ProblemTypesToJson {
}
}
public void updatePackageJson(File packageJsonFile, String propertyPrefix) throws Exception {
public void updatePackageJson(File packageJsonFile, String propertyPrefix, String id, ProblemType[] problemTypes) throws Exception {
Gson gson = new GsonBuilder()
.setPrettyPrinting()
.serializeNulls()
.create();
JsonElement parsed = gson.fromJson(new FileReader(packageJsonFile), JsonElement.class);
JsonObject configProps = (JsonObject) YamlPath.fromProperty("contributes.configuration.properties").traverse(new JSONCursor(parsed)).target;
Set<String> removeProps = configProps.keySet().stream().filter(s -> s.startsWith(propertyPrefix)).collect(Collectors.toSet());
for (String rp : removeProps) {
configProps.remove(rp);
}
JsonArray severities = new JsonArray();
for (ProblemSeverity severity : ProblemSeverity.values()) {
severities.add(severity.name());
}
for (Entry<String, ProblemTypeData[]> e : problemTypes.entrySet()) {
String editorType = e.getKey();
for (ProblemTypeData data : e.getValue()) {
JsonArray allProps = (JsonArray) YamlPath.fromProperty("contributes.configuration.properties").traverse(new JSONCursor(parsed)).target;
JsonObject configProps = Streams.fromIterable(allProps).filter(JsonObject.class::isInstance).map(JsonObject.class::cast).filter(o -> {
JsonElement element = o.get("id");
return element != null && id.equals(element.getAsString());
}).findFirst().orElse(null);
if (configProps != null) {
Set<String> removeProps = configProps.keySet().stream().filter(s -> s.startsWith(propertyPrefix)).collect(Collectors.toSet());
for (String rp : removeProps) {
configProps.remove(rp);
}
JsonArray severities = new JsonArray();
for (ProblemSeverity severity : ProblemSeverity.values()) {
severities.add(severity.name());
}
for (ProblemType problemType : problemTypes) {
ProblemTypeData data = new ProblemTypeData(problemType);
JsonObject schema = new JsonObject();
schema.addProperty("type", "string");
schema.addProperty("default", data.defaultSeverity);
schema.addProperty("description", data.description);
schema.add("enum", severities);
configProps.add(propertyPrefix+"."+editorType+"."+data.code, schema);
configProps.add(propertyPrefix + "." + id + "." + data.code, schema);
}
String newContent = gson.toJson(parsed);
FileUtils.writeStringToFile(packageJsonFile, newContent);
} else {
throw new IllegalStateException("Cannot find config object with 'id' property equal to '" + id + "'");
}
}
public void updatePackageJson(File packageJsonFile, String propertyPrefix) throws Exception {
Gson gson = new GsonBuilder()
.setPrettyPrinting()
.disableHtmlEscaping()
.serializeNulls()
.create();
JsonElement parsed = gson.fromJson(new FileReader(packageJsonFile), JsonElement.class);
JsonArray allProps = (JsonArray) YamlPath.fromProperty("contributes.configuration").traverse(new JSONCursor(parsed)).target;
JsonArray severities = new JsonArray();
for (ProblemSeverity severity : ProblemSeverity.values()) {
severities.add(severity.name());
}
for (ProblemCategoryData category : problemCategories) {
JsonObject configProps = Streams.fromIterable(allProps).filter(JsonObject.class::isInstance).map(JsonObject.class::cast).filter(o -> {
JsonElement element = o.get("id");
return element != null && category.getId().equals(element.getAsString());
}).findFirst().orElse(null);
if (configProps != null) {
allProps.remove(configProps);
}
}
for (ProblemCategoryData category : problemCategories) {
JsonObject configProps = new JsonObject();
configProps.addProperty("id", category.getId());
configProps.addProperty("title", category.getLabel());
configProps.addProperty("order", 400 + category.order);
JsonObject props = new JsonObject();
if (category.getToggle() != null) {
Toggle categorySwitch = category.getToggle();
JsonObject switchObj = new JsonObject();
switchObj.addProperty("type", "string");
switchObj.addProperty("default", categorySwitch.getDefaultValue().name());
switchObj.addProperty("description", categorySwitch.getLabel());
JsonArray vals = new JsonArray();
for (Toggle.Option s : categorySwitch.getValues()) {
vals.add(s.name());
}
switchObj.add("enum", vals);
props.add(categorySwitch.getPreferenceKey(), switchObj);
}
for (ProblemTypeData data : category.getProblemTypes()) {
JsonObject schema = new JsonObject();
schema.addProperty("type", "string");
schema.addProperty("default", data.defaultSeverity);
schema.addProperty("description", data.description);
schema.add("enum", severities);
props.add(propertyPrefix + "." + category.getId() + "." + data.code, schema);
}
configProps.add("properties", props);
allProps.add(configProps);
}
String newContent = gson.toJson(parsed);
FileUtils.writeStringToFile(packageJsonFile, newContent);
}

File diff suppressed because it is too large Load Diff