initial version of SpEL syntax validation
This commit is contained in:
@@ -97,6 +97,7 @@ public class BootJavaPreferencesPage extends FieldEditorPreferencePage implement
|
||||
});
|
||||
addField(new BooleanFieldEditor(Constants.PREF_CHANGE_DETECTION, "Live Boot Change Detection", fieldEditorParent));
|
||||
|
||||
addField(new BooleanFieldEditor(Constants.PREF_VALIDATION_SPEL_EXPRESSIONS, "SpEL Expression Syntax Validation", fieldEditorParent));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ public class Constants {
|
||||
|
||||
public static final String PREF_SCAN_JAVA_TEST_SOURCES = "boot-java.scan-java-test-sources";
|
||||
|
||||
public static final String PREF_VALIDATION_SPEL_EXPRESSIONS = "boot-java.validation.spel.on";
|
||||
|
||||
public static final String PREF_SUPPORT_SPRING_XML_CONFIGS = "boot-java.support-spring-xml-config.on";
|
||||
public static final String PREF_XML_CONFIGS_SCAN_FOLDERS = "boot-java.support-spring-xml-config.scan-folders-globs";
|
||||
public static final String PREF_XML_CONFIGS_HYPERLINKS = "boot-java.support-spring-xml-config.hyperlinks";
|
||||
|
||||
@@ -153,6 +153,8 @@ public class DelegatingStreamConnectionProvider implements StreamConnectionProvi
|
||||
Map<String, Object> supportXML = new HashMap<>();
|
||||
Map<String, Object> bootChangeDetection = new HashMap<>();
|
||||
Map<String, Object> scanTestJavaSources = new HashMap<>();
|
||||
Map<String, Object> validation = new HashMap<>();
|
||||
Map<String, Object> validationSpelExpressions = new HashMap<>();
|
||||
|
||||
IPreferenceStore preferenceStore = BootLanguageServerPlugin.getDefault().getPreferenceStore();
|
||||
|
||||
@@ -172,10 +174,15 @@ public class DelegatingStreamConnectionProvider implements StreamConnectionProvi
|
||||
bootChangeDetection.put("on", preferenceStore.getBoolean(Constants.PREF_CHANGE_DETECTION));
|
||||
scanTestJavaSources.put("on", preferenceStore.getBoolean(Constants.PREF_SCAN_JAVA_TEST_SOURCES));
|
||||
|
||||
validationSpelExpressions.put("on", preferenceStore.getBoolean(Constants.PREF_VALIDATION_SPEL_EXPRESSIONS));
|
||||
validation.put("spel", validationSpelExpressions);
|
||||
|
||||
bootJavaObj.put("live-information", liveInformation);
|
||||
bootJavaObj.put("support-spring-xml-config", supportXML);
|
||||
bootJavaObj.put("change-detection", bootChangeDetection);
|
||||
bootJavaObj.put("scan-java-test-sources", scanTestJavaSources);
|
||||
bootJavaObj.put("change-detection", bootChangeDetection);
|
||||
bootJavaObj.put("validation", validation);
|
||||
|
||||
bootJavaObj.put("remote-apps", getAllRemoteApps());
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ public class PrefsInitializer extends AbstractPreferenceInitializer {
|
||||
preferenceStore.setDefault(Constants.PREF_XML_CONFIGS_SCAN_FOLDERS, "src/main");
|
||||
|
||||
preferenceStore.setDefault(Constants.PREF_CHANGE_DETECTION, false);
|
||||
preferenceStore.setDefault(Constants.PREF_VALIDATION_SPEL_EXPRESSIONS, true);
|
||||
|
||||
preferenceStore.setDefault(Constants.PREF_SCAN_JAVA_TEST_SOURCES, false);
|
||||
}
|
||||
|
||||
@@ -35,6 +35,8 @@ public class BootJavaConfig implements InitializingBean {
|
||||
|
||||
public static final int LIVE_INFORMATION_FETCH_DATA_RETRY_MAX_NO_DEFAULT = 10;
|
||||
public static final int LIVE_INFORMATION_FETCH_DATA_RETRY_DELAY_IN_SECONDS_DEFAULT = 3;
|
||||
|
||||
public static final boolean VALIDAITON_SPEL_EXPRESSIONS_ENABLED_DEFAULT = true;
|
||||
|
||||
|
||||
//TODO: Consider changing this to something that raises Spring application events.
|
||||
@@ -110,6 +112,11 @@ public class BootJavaConfig implements InitializingBean {
|
||||
return enabled != null && enabled.booleanValue();
|
||||
}
|
||||
|
||||
public boolean isSpelExpressionValidationEnabled() {
|
||||
Boolean enabled = settings.getBoolean("boot-java", "validation", "spel", "on");
|
||||
return enabled != null ? enabled.booleanValue() : VALIDAITON_SPEL_EXPRESSIONS_ENABLED_DEFAULT;
|
||||
}
|
||||
|
||||
public boolean areXmlHyperlinksEnabled() {
|
||||
Boolean enabled = settings.getBoolean("boot-java", "support-spring-xml-config", "hyperlinks");
|
||||
return enabled != null && enabled.booleanValue();
|
||||
|
||||
@@ -15,6 +15,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.lsp4j.CompletionItemKind;
|
||||
@@ -32,6 +33,7 @@ import org.springframework.ide.vscode.boot.java.handlers.BootJavaCompletionEngin
|
||||
import org.springframework.ide.vscode.boot.java.handlers.BootJavaDocumentHighlightEngine;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.BootJavaDocumentSymbolHandler;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.BootJavaHoverProvider;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.BootJavaReconcileEngine;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.BootJavaReferencesHandler;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.BootJavaWorkspaceSymbolHandler;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.CodeLensProvider;
|
||||
@@ -70,6 +72,7 @@ import org.springframework.ide.vscode.commons.languageserver.completion.IComplet
|
||||
import org.springframework.ide.vscode.commons.languageserver.composable.LanguageServerComponents;
|
||||
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.reconcile.IReconcileEngine;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.CodeLensHandler;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.DocumentHighlightHandler;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.HoverHandler;
|
||||
@@ -103,6 +106,8 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent
|
||||
|
||||
private final SimpleLanguageServer server;
|
||||
private final BootLanguageServerParams serverParams;
|
||||
private final BootJavaConfig config;
|
||||
|
||||
private final SpringPropertyIndexProvider propertyIndexProvider;
|
||||
private final ProjectBasedPropertyIndexProvider adHocPropertyIndexProvider;
|
||||
|
||||
@@ -120,7 +125,6 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent
|
||||
|
||||
private SpringProcessTracker liveProcessTracker;
|
||||
|
||||
|
||||
public BootJavaLanguageServerComponents(
|
||||
SimpleLanguageServer server,
|
||||
BootLanguageServerParams serverParams,
|
||||
@@ -134,6 +138,7 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent
|
||||
) {
|
||||
this.server = server;
|
||||
this.serverParams = serverParams;
|
||||
this.config = config;
|
||||
|
||||
projectFinder = serverParams.projectFinder;
|
||||
projectObserver = serverParams.projectObserver;
|
||||
@@ -197,7 +202,7 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent
|
||||
|
||||
highlightsEngine = createDocumentHighlightEngine(indexer);
|
||||
documents.onDocumentHighlight(highlightsEngine);
|
||||
|
||||
|
||||
config.addListener(ignore -> {
|
||||
log.info("update live process tracker settings - start");
|
||||
|
||||
@@ -245,6 +250,11 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent
|
||||
public DocumentHighlightHandler getDocumentHighlightHandler() {
|
||||
return highlightsEngine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<IReconcileEngine> getReconcileEngine() {
|
||||
return Optional.of(new BootJavaReconcileEngine(this, config));
|
||||
}
|
||||
|
||||
private void initialized() {
|
||||
this.liveProcessTracker.start();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016-2017 Pivotal, Inc.
|
||||
* Copyright (c) 2016, 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
|
||||
@@ -10,8 +10,30 @@
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.handlers;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTVisitor;
|
||||
import org.eclipse.jdt.core.dom.CompilationUnit;
|
||||
import org.eclipse.jdt.core.dom.Expression;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
|
||||
import org.eclipse.jdt.core.dom.StringLiteral;
|
||||
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.expression.ParseException;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.ide.vscode.boot.app.BootJavaConfig;
|
||||
import org.springframework.ide.vscode.boot.java.BootJavaLanguageServerComponents;
|
||||
import org.springframework.ide.vscode.boot.java.value.Constants;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IReconcileEngine;
|
||||
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.ide.vscode.commons.util.text.IDocument;
|
||||
|
||||
/**
|
||||
@@ -19,8 +41,104 @@ import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
*/
|
||||
public class BootJavaReconcileEngine implements IReconcileEngine {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(BootJavaReconcileEngine.class);
|
||||
|
||||
private final JavaProjectFinder projectFinder;
|
||||
private final BootJavaLanguageServerComponents server;
|
||||
private final BootJavaConfig config;
|
||||
|
||||
public BootJavaReconcileEngine(BootJavaLanguageServerComponents server, BootJavaConfig config) {
|
||||
this.server = server;
|
||||
this.config = config;
|
||||
this.projectFinder = this.server.getProjectFinder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reconcile(IDocument doc, IProblemCollector problemCollector) {
|
||||
public void reconcile(final IDocument doc, final IProblemCollector problemCollector) {
|
||||
System.out.println("reconcile document: " + doc);
|
||||
|
||||
IJavaProject project = projectFinder.find(new TextDocumentIdentifier(doc.getUri())).orElse(null);
|
||||
URI uri = URI.create(doc.getUri());
|
||||
|
||||
if (project != null) {
|
||||
|
||||
try {
|
||||
problemCollector.beginCollecting();
|
||||
|
||||
server.getCompilationUnitCache().withCompilationUnit(project, uri, cu -> {
|
||||
if (cu != null) {
|
||||
reconcileAST(cu, problemCollector);
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
}
|
||||
finally {
|
||||
problemCollector.endCollecting();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void reconcileAST(CompilationUnit cu, IProblemCollector problemCollector) {
|
||||
cu.accept(new ASTVisitor() {
|
||||
|
||||
@Override
|
||||
public boolean visit(SingleMemberAnnotation node) {
|
||||
try {
|
||||
visitAnnotation(node, problemCollector);
|
||||
}
|
||||
catch (Exception e) {
|
||||
}
|
||||
return super.visit(node);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void visitAnnotation(SingleMemberAnnotation node, IProblemCollector problemCollector) {
|
||||
if (!config.isSpelExpressionValidationEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ITypeBinding typeBinding = node.resolveTypeBinding();
|
||||
|
||||
if (typeBinding != null) {
|
||||
String qname = typeBinding.getQualifiedName();
|
||||
if (Constants.SPRING_VALUE.equals(qname)) {
|
||||
Expression valueExp = node.getValue();
|
||||
|
||||
if (valueExp instanceof StringLiteral) {
|
||||
String value = ((StringLiteral) valueExp).getLiteralValue();
|
||||
if (value != null && value.startsWith("#{") && value.endsWith("}")) {
|
||||
reconcileSpELExpressionValue(value, valueExp, problemCollector);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void reconcileSpELExpressionValue(String value, Expression valueExp, IProblemCollector problemCollector) {
|
||||
String spelExpression = value.substring(2, value.length() - 1);
|
||||
|
||||
if (spelExpression.length() > 0) {
|
||||
SpelExpressionParser parser = new SpelExpressionParser();
|
||||
try {
|
||||
parser.parseExpression(spelExpression);
|
||||
}
|
||||
catch (ParseException e) {
|
||||
String message = e.getSimpleMessage();
|
||||
int position = e.getPosition();
|
||||
|
||||
createProblem(valueExp, message, position, problemCollector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void createProblem(Expression valueExp, String message, int position, IProblemCollector problemCollector) {
|
||||
int start = valueExp.getStartPosition() + 3 + position;
|
||||
int length = valueExp.getLength() - 5 - position;
|
||||
|
||||
ReconcileProblem problem = new ReconcileProblemImpl(ProblemTypes.create("SpEL Expression Problem", ProblemSeverity.ERROR), message, start, length);
|
||||
problemCollector.accept(problem);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -70,6 +70,11 @@ export const BootConfigSchema: PreferenceSchema = {
|
||||
description: 'Enable/Disable detecting changes of running Spring Boot applications.',
|
||||
default: false
|
||||
},
|
||||
'boot-java.validation.spel.on': {
|
||||
type: 'boolean',
|
||||
description: 'Validation - Validate SpEL Expression Syntax',
|
||||
default: true
|
||||
},
|
||||
'boot-java.highlight-codelens.on': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
@@ -99,6 +104,7 @@ export interface BootConfiguration {
|
||||
'boot-java.support-spring-xml-config.content-assist': boolean;
|
||||
'boot-java.support-spring-xml-config.scan-folders': string;
|
||||
'boot-java.change-detection.on': boolean;
|
||||
'boot-java.validation.spel.on': boolean;
|
||||
'boot-java.highlight-codelens.on': boolean;
|
||||
'spring-boot.ls.javahome': string;
|
||||
'spring-boot.ls.vmargs': string;
|
||||
|
||||
@@ -129,6 +129,11 @@
|
||||
"default": false,
|
||||
"description": "Enable/Disable detecting changes of running Spring Boot applications"
|
||||
},
|
||||
"boot-java.validation.spel.on": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Validation - Validate SpEL Expression Syntax"
|
||||
},
|
||||
"boot-java.remote-apps": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
|
||||
Reference in New Issue
Block a user