Move all non-conflicting source files from boot-java-language-server to
commons-boot
This commit is contained in:
@@ -1,5 +1,2 @@
|
||||
eclipse.preferences.version=1
|
||||
encoding//src/main/java=UTF-8
|
||||
encoding//src/test/java=UTF-8
|
||||
encoding//src/test/resources=UTF-8
|
||||
encoding/<project>=UTF-8
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java;
|
||||
|
||||
/**
|
||||
* Constants containing various fully-qualified annotation names.
|
||||
*
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
public class Annotations {
|
||||
public static final String BEAN = "org.springframework.context.annotation.Bean";
|
||||
public static final String PROFILE = "org.springframework.context.annotation.Profile";
|
||||
public static final String COMPONENT = "org.springframework.stereotype.Component";
|
||||
public static final String AUTOWIRED = "org.springframework.beans.factory.annotation.Autowired";
|
||||
|
||||
public static final String SPRING_REQUEST_MAPPING = "org.springframework.web.bind.annotation.RequestMapping";
|
||||
public static final String SPRING_GET_MAPPING = "org.springframework.web.bind.annotation.GetMapping";
|
||||
public static final String SPRING_POST_MAPPING = "org.springframework.web.bind.annotation.PostMapping";
|
||||
public static final String SPRING_PUT_MAPPING = "org.springframework.web.bind.annotation.PutMapping";
|
||||
public static final String SPRING_DELETE_MAPPING = "org.springframework.web.bind.annotation.DeleteMapping";
|
||||
public static final String SPRING_PATCH_MAPPING = "org.springframework.web.bind.annotation.PatchMapping";
|
||||
|
||||
public static final String CONDITIONAL = "org.springframework.context.annotation.Conditional";
|
||||
public static final String CONDITIONAL_ON_BEAN = "org.springframework.boot.autoconfigure.condition.ConditionalOnBean";
|
||||
public static final String CONDITIONAL_ON_MISSING_BEAN = "org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean";
|
||||
public static final String CONDITIONAL_ON_PROPERTY = "org.springframework.boot.autoconfigure.condition.ConditionalOnProperty";
|
||||
public static final String CONDITIONAL_ON_RESOURCE = "org.springframework.boot.autoconfigure.condition.ConditionalOnResource";
|
||||
public static final String CONDITIONAL_ON_CLASS = "org.springframework.boot.autoconfigure.condition.ConditionalOnClass";
|
||||
public static final String CONDITIONAL_ON_MISSING_CLASS = "org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass";
|
||||
public static final String CONDITIONAL_ON_CLOUD_PLATFORM = "org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform";
|
||||
public static final String CONDITIONAL_ON_WEB_APPLICATION = "org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication";
|
||||
public static final String CONDITIONAL_ON_NOT_WEB_APPLICATION = "org.springframework.boot.autoconfigure.condition.ConditionalOnNotWebApplication";
|
||||
public static final String CONDITIONAL_ON_ENABLED_INFO_CONTRIBUTOR = "org.springframework.boot.actuate.autoconfigure.ConditionalOnEnabledInfoContributor";
|
||||
public static final String CONDITIONAL_ON_ENABLED_RESOURCE_CHAIN = "org.springframework.boot.autoconfigure.web.ConditionalOnEnabledResourceChain";
|
||||
public static final String CONDITIONAL_ON_ENABLED_ENDPOINT = "org.springframework.boot.actuate.condition.ConditionalOnEnabledEndpoint";
|
||||
public static final String CONDITIONAL_ON_ENABLED_HEALTH_INDICATOR = "org.springframework.boot.actuate.autoconfigure.ConditionalOnEnabledHealthIndicator";
|
||||
public static final String CONDITIONAL_ON_EXPRESSION = "org.springframework.boot.autoconfigure.condition.ConditionalOnExpression";
|
||||
public static final String CONDITIONAL_ON_JAVA = "org.springframework.boot.autoconfigure.condition.ConditionalOnJava";
|
||||
public static final String CONDITIONAL_ON_JNDI = "org.springframework.boot.autoconfigure.condition.ConditionalOnJndi";
|
||||
public static final String CONDITIONAL_ON_SINGLE_CANDIDATE = "org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate";
|
||||
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java;
|
||||
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.Settings;
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
|
||||
/**
|
||||
* Boot-Java LS settings
|
||||
*
|
||||
* @author Alex Boyko
|
||||
*
|
||||
*/
|
||||
public class BootJavaConfig {
|
||||
|
||||
private Settings settings = new Settings(null);
|
||||
|
||||
public boolean isBootHintsEnabled() {
|
||||
Boolean enabled = (Boolean) settings.getProperty("boot-java", "boot-hints", "on");
|
||||
return enabled == null || enabled.booleanValue();
|
||||
}
|
||||
|
||||
public void handleConfigurationChange(Settings newConfig) {
|
||||
Log.info("Settings received: "+newConfig);
|
||||
this.settings = newConfig;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,346 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016, 2018 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.eclipse.lsp4j.CompletionItemKind;
|
||||
import org.eclipse.lsp4j.InitializeParams;
|
||||
import org.eclipse.lsp4j.InitializeResult;
|
||||
import org.eclipse.lsp4j.Registration;
|
||||
import org.eclipse.lsp4j.RegistrationParams;
|
||||
import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchyAwareLookup;
|
||||
import org.springframework.ide.vscode.boot.java.autowired.AutowiredHoverProvider;
|
||||
import org.springframework.ide.vscode.boot.java.beans.BeansSymbolProvider;
|
||||
import org.springframework.ide.vscode.boot.java.beans.ComponentSymbolProvider;
|
||||
import org.springframework.ide.vscode.boot.java.conditionals.ConditionalsLiveHoverProvider;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.BootJavaCodeLensEngine;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.BootJavaCompletionEngine;
|
||||
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.CompletionProvider;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.HoverProvider;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.ReferenceProvider;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.RunningAppProvider;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider;
|
||||
import org.springframework.ide.vscode.boot.java.livehover.ActiveProfilesProvider;
|
||||
import org.springframework.ide.vscode.boot.java.livehover.BeanInjectedIntoHoverProvider;
|
||||
import org.springframework.ide.vscode.boot.java.livehover.ComponentInjectionsHoverProvider;
|
||||
import org.springframework.ide.vscode.boot.java.metadata.SpringPropertyIndexProvider;
|
||||
import org.springframework.ide.vscode.boot.java.requestmapping.LiveAppURLSymbolProvider;
|
||||
import org.springframework.ide.vscode.boot.java.requestmapping.RequestMappingHoverProvider;
|
||||
import org.springframework.ide.vscode.boot.java.requestmapping.RequestMappingSymbolProvider;
|
||||
import org.springframework.ide.vscode.boot.java.scope.ScopeCompletionProcessor;
|
||||
import org.springframework.ide.vscode.boot.java.snippets.JavaSnippet;
|
||||
import org.springframework.ide.vscode.boot.java.snippets.JavaSnippetContext;
|
||||
import org.springframework.ide.vscode.boot.java.snippets.JavaSnippetManager;
|
||||
import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache;
|
||||
import org.springframework.ide.vscode.boot.java.utils.SpringIndexer;
|
||||
import org.springframework.ide.vscode.boot.java.utils.SpringLiveHoverWatchdog;
|
||||
import org.springframework.ide.vscode.boot.java.value.ValueCompletionProcessor;
|
||||
import org.springframework.ide.vscode.boot.java.value.ValueHoverProvider;
|
||||
import org.springframework.ide.vscode.boot.java.value.ValuePropertyReferencesProvider;
|
||||
import org.springframework.ide.vscode.commons.languageserver.HighlightParams;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionEngine;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.VscodeCompletionEngineAdapter;
|
||||
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.multiroot.WorkspaceFoldersProposedService;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IReconcileEngine;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.LSFactory;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.ReferencesHandler;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServerWrapper;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleWorkspaceService;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
/**
|
||||
* Language Server for Spring Boot Application Properties files
|
||||
*
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class BootJavaLanguageServer extends SimpleLanguageServer {
|
||||
|
||||
private final VscodeCompletionEngineAdapter completionEngine;
|
||||
private final SpringIndexer indexer;
|
||||
private final SpringPropertyIndexProvider propertyIndexProvider;
|
||||
private final SpringLiveHoverWatchdog liveHoverWatchdog;
|
||||
private final ProjectObserver projectObserver;
|
||||
private final BootJavaConfig config;
|
||||
private final CompilationUnitCache cuCache;
|
||||
|
||||
private final WordHighlighter testHightlighter = null; // new WordHighlighter("foo");
|
||||
|
||||
private JavaProjectFinder projectFinder;
|
||||
|
||||
public BootJavaLanguageServer(LSFactory<BootJavaLanguageServerParams> _params) {
|
||||
super("boot-java");
|
||||
BootJavaLanguageServerParams serverParams = _params.create(this);
|
||||
|
||||
this.config = new BootJavaConfig();
|
||||
|
||||
projectFinder = serverParams.projectFinder;
|
||||
projectObserver = serverParams.projectObserver;
|
||||
cuCache = new CompilationUnitCache(projectFinder, getTextDocumentService(), projectObserver);
|
||||
|
||||
propertyIndexProvider = serverParams.indexProvider;
|
||||
|
||||
JavaProjectFinder javaProjectFinder = serverParams.projectFinder;
|
||||
SimpleWorkspaceService workspaceService = getWorkspaceService();
|
||||
SimpleTextDocumentService documents = getTextDocumentService();
|
||||
|
||||
IReconcileEngine reconcileEngine = new BootJavaReconcileEngine();
|
||||
documents.onDidChangeContent(params -> {
|
||||
TextDocument doc = params.getDocument();
|
||||
validateWith(doc.getId(), reconcileEngine);
|
||||
});
|
||||
|
||||
ICompletionEngine bootCompletionEngine = createCompletionEngine(javaProjectFinder, propertyIndexProvider);
|
||||
completionEngine = createCompletionEngineAdapter(this, bootCompletionEngine);
|
||||
completionEngine.setMaxCompletions(100);
|
||||
documents.onCompletion(completionEngine::getCompletions);
|
||||
documents.onCompletionResolve(completionEngine::resolveCompletion);
|
||||
|
||||
BootJavaHoverProvider hoverInfoProvider = createHoverHandler(javaProjectFinder,
|
||||
serverParams.runningAppProvider);
|
||||
documents.onHover(hoverInfoProvider);
|
||||
|
||||
ReferencesHandler referencesHandler = createReferenceHandler(this, javaProjectFinder);
|
||||
documents.onReferences(referencesHandler);
|
||||
|
||||
indexer = createAnnotationIndexer(this, javaProjectFinder);
|
||||
documents.onDidSave(params -> {
|
||||
String docURI = params.getDocument().getId().getUri();
|
||||
String content = params.getDocument().get();
|
||||
indexer.updateDocument(docURI, content);
|
||||
});
|
||||
|
||||
documents.onDocumentSymbol(new BootJavaDocumentSymbolHandler(indexer));
|
||||
workspaceService.onWorkspaceSymbol(new BootJavaWorkspaceSymbolHandler(indexer,
|
||||
new LiveAppURLSymbolProvider(serverParams.runningAppProvider)));
|
||||
|
||||
BootJavaCodeLensEngine codeLensHandler = createCodeLensEngine(this, javaProjectFinder);
|
||||
documents.onCodeLens(codeLensHandler::createCodeLenses);
|
||||
documents.onCodeLensResolve(codeLensHandler::resolveCodeLens);
|
||||
|
||||
liveHoverWatchdog = new SpringLiveHoverWatchdog(this, hoverInfoProvider, serverParams.runningAppProvider,
|
||||
projectFinder, projectObserver, serverParams.watchDogInterval);
|
||||
documents.onDidChangeContent(params -> {
|
||||
TextDocument doc = params.getDocument();
|
||||
if (testHightlighter != null) {
|
||||
getClient().highlight(new HighlightParams(params.getDocument().getId(), testHightlighter.apply(doc)));
|
||||
} else {
|
||||
liveHoverWatchdog.watchDocument(doc.getUri());
|
||||
liveHoverWatchdog.update(doc.getUri(), null);
|
||||
}
|
||||
});
|
||||
|
||||
documents.onDidClose(doc -> {
|
||||
if (testHightlighter != null) {
|
||||
getClient().highlight(new HighlightParams(doc.getId(), testHightlighter.apply(doc)));
|
||||
} else {
|
||||
liveHoverWatchdog.unwatchDocument(doc.getUri());
|
||||
}
|
||||
});
|
||||
|
||||
workspaceService.onDidChangeConfiguraton(settings -> {
|
||||
config.handleConfigurationChange(settings);
|
||||
if (config.isBootHintsEnabled()) {
|
||||
liveHoverWatchdog.enableHighlights();
|
||||
} else {
|
||||
liveHoverWatchdog.disableHighlights();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public void setMaxCompletionsNumber(int number) {
|
||||
completionEngine.setMaxCompletions(number);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<InitializeResult> initialize(InitializeParams params) {
|
||||
CompletableFuture<InitializeResult> result = super.initialize(params);
|
||||
|
||||
this.indexer.initialize(getWorkspaceRoots());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialized() {
|
||||
Registration registration = new Registration(WorkspaceFoldersProposedService.CAPABILITY_ID, WorkspaceFoldersProposedService.CAPABILITY_NAME, null);
|
||||
RegistrationParams registrationParams = new RegistrationParams(Collections.singletonList(registration));
|
||||
getClient().registerCapability(registrationParams);
|
||||
|
||||
this.indexer.serverInitialized();
|
||||
|
||||
// TODO: due to a missing message from lsp4e this "initialized" is not called in
|
||||
// the LSP4E case
|
||||
// if this gets fixed, the code should move here (from "initialize" above)
|
||||
|
||||
// this.indexer.initialize(this.getWorkspaceRoot());
|
||||
// this.liveHoverWatchdog.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Object> shutdown() {
|
||||
this.liveHoverWatchdog.shutdown();
|
||||
this.indexer.shutdown();
|
||||
this.cuCache.dispose();
|
||||
|
||||
return super.shutdown();
|
||||
}
|
||||
|
||||
protected ICompletionEngine createCompletionEngine(JavaProjectFinder javaProjectFinder,
|
||||
SpringPropertyIndexProvider indexProvider) {
|
||||
Map<String, CompletionProvider> providers = new HashMap<>();
|
||||
providers.put(org.springframework.ide.vscode.boot.java.scope.Constants.SPRING_SCOPE,
|
||||
new ScopeCompletionProcessor());
|
||||
providers.put(org.springframework.ide.vscode.boot.java.value.Constants.SPRING_VALUE,
|
||||
new ValueCompletionProcessor(indexProvider));
|
||||
|
||||
JavaSnippetManager snippetManager = new JavaSnippetManager(this::createSnippetBuilder);
|
||||
snippetManager.add(
|
||||
new JavaSnippet("RequestMapping method", JavaSnippetContext.BOOT_MEMBERS, CompletionItemKind.Method,
|
||||
ImmutableList.of("org.springframework.web.bind.annotation.RequestMapping",
|
||||
"org.springframework.web.bind.annotation.RequestMethod",
|
||||
"org.springframework.web.bind.annotation.RequestParam"),
|
||||
"@RequestMapping(value=\"${path}\", method=RequestMethod.${GET})\n"
|
||||
+ "public ${SomeData} ${requestMethodName}(@RequestParam ${String} ${param}) {\n"
|
||||
+ " return new ${SomeData}(${cursor});\n" + "}\n"));
|
||||
snippetManager
|
||||
.add(new JavaSnippet("GetMapping method", JavaSnippetContext.BOOT_MEMBERS, CompletionItemKind.Method,
|
||||
ImmutableList.of("org.springframework.web.bind.annotation.GetMapping",
|
||||
"org.springframework.web.bind.annotation.RequestParam"),
|
||||
"@GetMapping(value=\"${path}\")\n"
|
||||
+ "public ${SomeData} ${getMethodName}(@RequestParam ${String} ${param}) {\n"
|
||||
+ " return new ${SomeData}(${cursor});\n" + "}\n"));
|
||||
snippetManager.add(new JavaSnippet("PostMapping method", JavaSnippetContext.BOOT_MEMBERS,
|
||||
CompletionItemKind.Method,
|
||||
ImmutableList.of("org.springframework.web.bind.annotation.PostMapping",
|
||||
"org.springframework.web.bind.annotation.RequestBody"),
|
||||
"@PostMapping(value=\"${path}\")\n"
|
||||
+ "public ${SomeEnityData} ${postMethodName}(@RequestBody ${SomeEnityData} ${entity}) {\n"
|
||||
+ " //TODO: process POST request\n" + " ${cursor}\n" + " return ${entity};\n" + "}\n"));
|
||||
snippetManager.add(new JavaSnippet("PutMapping method", JavaSnippetContext.BOOT_MEMBERS,
|
||||
CompletionItemKind.Method,
|
||||
ImmutableList.of("org.springframework.web.bind.annotation.PutMapping",
|
||||
"org.springframework.web.bind.annotation.RequestBody",
|
||||
"org.springframework.web.bind.annotation.PathVariable"),
|
||||
"@PutMapping(value=\"${path}/{${id}}\")\n"
|
||||
+ "public ${SomeEnityData} ${putMethodName}(@PathVariable ${pvt:String} ${id}, @RequestBody ${SomeEnityData} ${entity}) {\n"
|
||||
+ " //TODO: process PUT request\n" + " ${cursor}\n" + " return ${entity};\n" + "}"));
|
||||
return new BootJavaCompletionEngine(this, providers, snippetManager);
|
||||
}
|
||||
|
||||
protected BootJavaHoverProvider createHoverHandler(JavaProjectFinder javaProjectFinder,
|
||||
RunningAppProvider runningAppProvider) {
|
||||
AnnotationHierarchyAwareLookup<HoverProvider> providers = new AnnotationHierarchyAwareLookup<>();
|
||||
|
||||
providers.put(org.springframework.ide.vscode.boot.java.value.Constants.SPRING_VALUE, new ValueHoverProvider());
|
||||
|
||||
providers.put(Annotations.SPRING_REQUEST_MAPPING, new RequestMappingHoverProvider());
|
||||
providers.put(Annotations.SPRING_GET_MAPPING, new RequestMappingHoverProvider());
|
||||
providers.put(Annotations.SPRING_POST_MAPPING, new RequestMappingHoverProvider());
|
||||
providers.put(Annotations.SPRING_PUT_MAPPING, new RequestMappingHoverProvider());
|
||||
providers.put(Annotations.SPRING_DELETE_MAPPING, new RequestMappingHoverProvider());
|
||||
providers.put(Annotations.SPRING_PATCH_MAPPING, new RequestMappingHoverProvider());
|
||||
providers.put(Annotations.PROFILE, new ActiveProfilesProvider());
|
||||
|
||||
providers.put(Annotations.AUTOWIRED, new AutowiredHoverProvider());
|
||||
providers.put(Annotations.COMPONENT, new ComponentInjectionsHoverProvider());
|
||||
providers.put(Annotations.BEAN, new BeanInjectedIntoHoverProvider());
|
||||
|
||||
providers.put(Annotations.CONDITIONAL, new ConditionalsLiveHoverProvider());
|
||||
providers.put(Annotations.CONDITIONAL_ON_BEAN, new ConditionalsLiveHoverProvider());
|
||||
providers.put(Annotations.CONDITIONAL_ON_MISSING_BEAN, new ConditionalsLiveHoverProvider());
|
||||
providers.put(Annotations.CONDITIONAL_ON_PROPERTY, new ConditionalsLiveHoverProvider());
|
||||
providers.put(Annotations.CONDITIONAL_ON_RESOURCE, new ConditionalsLiveHoverProvider());
|
||||
providers.put(Annotations.CONDITIONAL_ON_CLASS, new ConditionalsLiveHoverProvider());
|
||||
providers.put(Annotations.CONDITIONAL_ON_MISSING_CLASS, new ConditionalsLiveHoverProvider());
|
||||
providers.put(Annotations.CONDITIONAL_ON_CLOUD_PLATFORM, new ConditionalsLiveHoverProvider());
|
||||
providers.put(Annotations.CONDITIONAL_ON_WEB_APPLICATION, new ConditionalsLiveHoverProvider());
|
||||
providers.put(Annotations.CONDITIONAL_ON_NOT_WEB_APPLICATION, new ConditionalsLiveHoverProvider());
|
||||
providers.put(Annotations.CONDITIONAL_ON_ENABLED_INFO_CONTRIBUTOR, new ConditionalsLiveHoverProvider());
|
||||
providers.put(Annotations.CONDITIONAL_ON_ENABLED_RESOURCE_CHAIN, new ConditionalsLiveHoverProvider());
|
||||
providers.put(Annotations.CONDITIONAL_ON_ENABLED_ENDPOINT, new ConditionalsLiveHoverProvider());
|
||||
providers.put(Annotations.CONDITIONAL_ON_ENABLED_HEALTH_INDICATOR, new ConditionalsLiveHoverProvider());
|
||||
providers.put(Annotations.CONDITIONAL_ON_EXPRESSION, new ConditionalsLiveHoverProvider());
|
||||
providers.put(Annotations.CONDITIONAL_ON_JAVA, new ConditionalsLiveHoverProvider());
|
||||
providers.put(Annotations.CONDITIONAL_ON_JNDI, new ConditionalsLiveHoverProvider());
|
||||
providers.put(Annotations.CONDITIONAL_ON_SINGLE_CANDIDATE, new ConditionalsLiveHoverProvider());
|
||||
|
||||
return new BootJavaHoverProvider(this, javaProjectFinder, providers, runningAppProvider);
|
||||
}
|
||||
|
||||
protected SpringIndexer createAnnotationIndexer(SimpleLanguageServer server, JavaProjectFinder projectFinder) {
|
||||
AnnotationHierarchyAwareLookup<SymbolProvider> providers = new AnnotationHierarchyAwareLookup<>();
|
||||
providers.put(Annotations.SPRING_REQUEST_MAPPING, new RequestMappingSymbolProvider());
|
||||
providers.put(Annotations.SPRING_GET_MAPPING, new RequestMappingSymbolProvider());
|
||||
providers.put(Annotations.SPRING_POST_MAPPING, new RequestMappingSymbolProvider());
|
||||
providers.put(Annotations.SPRING_PUT_MAPPING, new RequestMappingSymbolProvider());
|
||||
providers.put(Annotations.SPRING_DELETE_MAPPING, new RequestMappingSymbolProvider());
|
||||
providers.put(Annotations.SPRING_PATCH_MAPPING, new RequestMappingSymbolProvider());
|
||||
|
||||
providers.put(Annotations.BEAN, new BeansSymbolProvider());
|
||||
providers.put(Annotations.COMPONENT, new ComponentSymbolProvider());
|
||||
|
||||
return new SpringIndexer(this, projectFinder, providers);
|
||||
}
|
||||
|
||||
protected ReferencesHandler createReferenceHandler(SimpleLanguageServer server, JavaProjectFinder projectFinder) {
|
||||
Map<String, ReferenceProvider> providers = new HashMap<>();
|
||||
providers.put(org.springframework.ide.vscode.boot.java.value.Constants.SPRING_VALUE,
|
||||
new ValuePropertyReferencesProvider(server));
|
||||
|
||||
return new BootJavaReferencesHandler(server, projectFinder, providers);
|
||||
}
|
||||
|
||||
protected BootJavaCodeLensEngine createCodeLensEngine(SimpleLanguageServer server,
|
||||
JavaProjectFinder projectFinder) {
|
||||
return new BootJavaCodeLensEngine(server, projectFinder);
|
||||
}
|
||||
|
||||
public ProjectObserver getProjectObserver() {
|
||||
return projectObserver;
|
||||
}
|
||||
|
||||
public JavaProjectFinder getProjectFinder() {
|
||||
return projectFinder;
|
||||
}
|
||||
|
||||
public SpringIndexer getSpringIndexer() {
|
||||
return indexer;
|
||||
}
|
||||
|
||||
public SpringPropertyIndexProvider getSpringPropertyIndexProvider() {
|
||||
return propertyIndexProvider;
|
||||
}
|
||||
|
||||
public BootJavaConfig getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public CompilationUnitCache getCompilationUnitCache() {
|
||||
return cuCache;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.springframework.ide.vscode.boot.java.handlers.RunningAppProvider;
|
||||
import org.springframework.ide.vscode.boot.java.metadata.DefaultSpringPropertyIndexProvider;
|
||||
import org.springframework.ide.vscode.boot.java.metadata.SpringPropertyIndexProvider;
|
||||
import org.springframework.ide.vscode.boot.java.utils.SpringLiveHoverWatchdog;
|
||||
import org.springframework.ide.vscode.commons.gradle.GradleCore;
|
||||
import org.springframework.ide.vscode.commons.gradle.GradleProjectCache;
|
||||
import org.springframework.ide.vscode.commons.gradle.GradleProjectFinder;
|
||||
import org.springframework.ide.vscode.commons.java.BootProjectUtil;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.CompositeJavaProjectFinder;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.CompositeProjectOvserver;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.LSFactory;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||
import org.springframework.ide.vscode.commons.maven.MavenCore;
|
||||
import org.springframework.ide.vscode.commons.maven.java.MavenProjectCache;
|
||||
import org.springframework.ide.vscode.commons.maven.java.MavenProjectFinder;
|
||||
|
||||
public class BootJavaLanguageServerParams {
|
||||
|
||||
public final JavaProjectFinder projectFinder;
|
||||
public final ProjectObserver projectObserver;
|
||||
public final SpringPropertyIndexProvider indexProvider;
|
||||
public final RunningAppProvider runningAppProvider;
|
||||
public final Duration watchDogInterval;
|
||||
|
||||
public BootJavaLanguageServerParams(
|
||||
JavaProjectFinder projectFinder,
|
||||
ProjectObserver projectObserver,
|
||||
SpringPropertyIndexProvider indexProvider,
|
||||
RunningAppProvider runningAppProvider,
|
||||
Duration watchDogInterval
|
||||
) {
|
||||
super();
|
||||
this.projectFinder = projectFinder;
|
||||
this.projectObserver = projectObserver;
|
||||
this.indexProvider = indexProvider;
|
||||
this.runningAppProvider = runningAppProvider;
|
||||
this.watchDogInterval = watchDogInterval;
|
||||
}
|
||||
|
||||
public static LSFactory<BootJavaLanguageServerParams> createDefault() {
|
||||
return (SimpleLanguageServer server) -> {
|
||||
// Initialize project finders, project caches and project observers
|
||||
CompositeJavaProjectFinder javaProjectFinder = new CompositeJavaProjectFinder();
|
||||
MavenProjectCache mavenProjectCache = new MavenProjectCache(server, MavenCore.getDefault(), true, Paths.get(IJavaProject.PROJECT_CACHE_FOLDER));
|
||||
javaProjectFinder.addJavaProjectFinder(new MavenProjectFinder(mavenProjectCache));
|
||||
|
||||
GradleProjectCache gradleProjectCache = new GradleProjectCache(server, GradleCore.getDefault(), true, Paths.get(IJavaProject.PROJECT_CACHE_FOLDER));
|
||||
javaProjectFinder.addJavaProjectFinder(new GradleProjectFinder(gradleProjectCache));
|
||||
|
||||
CompositeProjectOvserver projectObserver = new CompositeProjectOvserver(Arrays.asList(mavenProjectCache, gradleProjectCache));
|
||||
|
||||
return new BootJavaLanguageServerParams(
|
||||
javaProjectFinder.filter(BootProjectUtil::isBootProject),
|
||||
projectObserver,
|
||||
new DefaultSpringPropertyIndexProvider(javaProjectFinder, projectObserver),
|
||||
RunningAppProvider.DEFAULT,
|
||||
SpringLiveHoverWatchdog.DEFAULT_INTERVAL
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
public static LSFactory<BootJavaLanguageServerParams> createTestDefault() {
|
||||
return (SimpleLanguageServer server) -> {
|
||||
// Initialize project finders, project caches and project observers
|
||||
CompositeJavaProjectFinder javaProjectFinder = new CompositeJavaProjectFinder();
|
||||
MavenProjectCache mavenProjectCache = new MavenProjectCache(server, MavenCore.getDefault(), false, null);
|
||||
mavenProjectCache.setAlwaysFireEventOnFileChanged(true);
|
||||
javaProjectFinder.addJavaProjectFinder(new MavenProjectFinder(mavenProjectCache));
|
||||
|
||||
GradleProjectCache gradleProjectCache = new GradleProjectCache(server, GradleCore.getDefault(), false, null);
|
||||
gradleProjectCache.setAlwaysFireEventOnFileChanged(true);
|
||||
javaProjectFinder.addJavaProjectFinder(new GradleProjectFinder(gradleProjectCache));
|
||||
|
||||
CompositeProjectOvserver projectObserver = new CompositeProjectOvserver(Arrays.asList(mavenProjectCache, gradleProjectCache));
|
||||
|
||||
return new BootJavaLanguageServerParams(
|
||||
javaProjectFinder.filter(BootProjectUtil::isBootProject),
|
||||
projectObserver,
|
||||
new DefaultSpringPropertyIndexProvider(javaProjectFinder, projectObserver),
|
||||
RunningAppProvider.DEFAULT,
|
||||
SpringLiveHoverWatchdog.DEFAULT_INTERVAL
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016, 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.ide.vscode.commons.languageserver.LaunguageServerApp;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||
import org.springframework.ide.vscode.commons.util.LogRedirect;
|
||||
|
||||
/**
|
||||
* Starts up Language Server process
|
||||
*
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) throws IOException, InterruptedException {
|
||||
String serverName = "boot-java-language-server";
|
||||
if (!System.getProperty(LaunguageServerApp.STANDALONE_STARTUP, "false").equals("true")) {
|
||||
LogRedirect.redirectToFile(serverName);
|
||||
}
|
||||
LaunguageServerApp.start(serverName, () -> {
|
||||
SimpleLanguageServer server = new BootJavaLanguageServer(
|
||||
BootJavaLanguageServerParams.createDefault()
|
||||
);
|
||||
return server;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java;
|
||||
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver;
|
||||
|
||||
public class ProjectServices {
|
||||
|
||||
public final JavaProjectFinder finder;
|
||||
public final ProjectObserver observer;
|
||||
|
||||
public ProjectServices(JavaProjectFinder finder, ProjectObserver observer) {
|
||||
super();
|
||||
this.finder = finder;
|
||||
this.observer = observer;
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||
import org.springframework.ide.vscode.commons.util.BadLocationException;
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
/**
|
||||
* Sample implementation of a 'highlighter' that can be used with {@link SimpleLanguageServer}.highlightWith.
|
||||
* <p>
|
||||
* Finds every occurence of a given word and highlights it.
|
||||
*
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
public class WordHighlighter implements Function<TextDocument, List<Range>> {
|
||||
|
||||
private final String word;
|
||||
|
||||
public WordHighlighter(String word) {
|
||||
super();
|
||||
this.word = word;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Range> apply(TextDocument doc) {
|
||||
String text = doc.get();
|
||||
int wordStart = text.indexOf(word);
|
||||
ImmutableList.Builder<Range> highlights = ImmutableList.builder();
|
||||
while (wordStart>=0) {
|
||||
try {
|
||||
highlights.add(doc.toRange(wordStart, word.length()));
|
||||
} catch (BadLocationException e) {
|
||||
Log.log(e);
|
||||
}
|
||||
wordStart = text.indexOf(word, wordStart+word.length());
|
||||
}
|
||||
return highlights.build();
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.annotations;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.IAnnotationBinding;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.springframework.ide.vscode.commons.util.CollectorUtil;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
/**
|
||||
* Utility class for working with annotation and discovering / understanding their
|
||||
* 'inheritance' structure.
|
||||
* <p>
|
||||
* Provides methods to ask questions about inheritance between annotations.
|
||||
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
public abstract class AnnotationHierarchies {
|
||||
|
||||
private AnnotationHierarchies() {
|
||||
}
|
||||
|
||||
protected static boolean ignoreAnnotation(String fqname) {
|
||||
return fqname.startsWith("java."); //mostly intended to capture java.lang.annotation.* types. But really it should be
|
||||
//safe to ignore any type defined by the JRE since it can't possibly be inheriting from a spring annotation.
|
||||
};
|
||||
|
||||
public static Collection<ITypeBinding> getDirectSuperAnnotations(ITypeBinding typeBinding) {
|
||||
IAnnotationBinding[] annotations = typeBinding.getAnnotations();
|
||||
if (annotations!=null && annotations.length!=0) {
|
||||
ImmutableList.Builder<ITypeBinding> superAnnotations = ImmutableList.builder();
|
||||
for (IAnnotationBinding ab : annotations) {
|
||||
ITypeBinding sa = ab.getAnnotationType();
|
||||
if (sa!=null) {
|
||||
if (!ignoreAnnotation(sa.getQualifiedName())) {
|
||||
superAnnotations.add(sa);
|
||||
}
|
||||
}
|
||||
}
|
||||
return superAnnotations.build();
|
||||
}
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
public static Set<String> getTransitiveSuperAnnotations(ITypeBinding typeBinding) {
|
||||
Set<String> seen = new HashSet<>();
|
||||
findTransitiveSupers(typeBinding, seen);
|
||||
return seen;
|
||||
}
|
||||
|
||||
public static Stream<ITypeBinding> findTransitiveSupers(ITypeBinding typeBinding, Set<String> seen) {
|
||||
String qname = typeBinding.getQualifiedName();
|
||||
if (seen.add(qname)) {
|
||||
return Stream.concat(
|
||||
Stream.of(typeBinding),
|
||||
getDirectSuperAnnotations(typeBinding).stream().flatMap(superBinding ->
|
||||
findTransitiveSupers(superBinding, seen)
|
||||
)
|
||||
);
|
||||
}
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
public static boolean isSubtypeOf(Annotation annotation, String fqAnnotationTypeName) {
|
||||
ITypeBinding annotationType = annotation.resolveTypeBinding();
|
||||
if (annotationType!=null) {
|
||||
return findTransitiveSupers(annotationType, new HashSet<>())
|
||||
.anyMatch(superType -> superType.getQualifiedName().equals(fqAnnotationTypeName));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Collection<ITypeBinding> getMetaAnnotations(ITypeBinding actualAnnotation, Predicate<String> isKeyAnnotationName) {
|
||||
Stream<ITypeBinding> allSupers = findTransitiveSupers(actualAnnotation, new HashSet<>())
|
||||
.skip(1); //Don't include 'actualAnnotation' itself.
|
||||
return allSupers
|
||||
.filter(candidate -> isMetaAnnotation(candidate, isKeyAnnotationName))
|
||||
.collect(CollectorUtil.toImmutableList());
|
||||
}
|
||||
|
||||
private static boolean isMetaAnnotation(ITypeBinding candidate, Predicate<String> isKeyAnnotationName) {
|
||||
return findTransitiveSupers(candidate, new HashSet<>())
|
||||
.anyMatch(sa -> isKeyAnnotationName.test(sa.getQualifiedName()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2018 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.annotations;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.springframework.ide.vscode.commons.util.Assert;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
/**
|
||||
* A Map-like utilty that allows putting and getting values associated with
|
||||
* annotation types.
|
||||
* <p>
|
||||
* The lookup is 'hierarchy aware' which means that is able to associate values
|
||||
* with a given type and all its subtypes all at once.
|
||||
*
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
public class AnnotationHierarchyAwareLookup<T> {
|
||||
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private static class Binding<T> {
|
||||
T value;
|
||||
boolean isOverriding;
|
||||
public Binding(T value, boolean isOverriding) {
|
||||
this.isOverriding = isOverriding;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Associates fq anotation type name to a Binding.
|
||||
*/
|
||||
private Map<String, Binding<T>> bindings = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Associates a value with a given annotation type (and all its subtypes implicitly).
|
||||
*
|
||||
* @param fqName Fully qualified type name for the annotation.
|
||||
* @param overrideSuperTypes Determines whether the binding has 'override' behavior. Override behavior
|
||||
* means that this binding stops the search for additional bindings associated
|
||||
* with a super type. If override behavior is disabled the search will continue
|
||||
* so that values associated with supertypes will also be found and returned
|
||||
* in addition to the more specific binding.
|
||||
* @param value
|
||||
*/
|
||||
public void put(String fqName, boolean overrideSuperTypes, T value) {
|
||||
Assert.isLegal(bindings.get(fqName)==null, "Multiple bindings to the same fqName are not supported");
|
||||
bindings.put(fqName, new Binding<>(value, overrideSuperTypes));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all associations applicable to a given annotationType. Note that a single 'put' binding for
|
||||
* a supertype can result in mutiple applicable associations for single annotation type because
|
||||
* a single put actually creates associations for a type and all its subtypes implicitly.
|
||||
* <p>
|
||||
* So, for example:
|
||||
* <code>
|
||||
* AnnotationHierarchyAwareLookup registry = new AnnotationHierarchyAwareLookup<String>();
|
||||
* registry.put("spring.annotation.Component", "ComponentProvider");
|
||||
* </code>
|
||||
* Now, assuming that RestController is a sub annotation of Controller which is a subtype of Component,
|
||||
* then if we call `get(...typeBinding of RestController...`) we will get back a collection of 3 elements:
|
||||
* ("spring.annotation.Component", "ComponentProvider"),
|
||||
* ("spring.annotation.Controller", "ComponentProvider")
|
||||
* ("spring.annotation.RestController", "ComponentProvider")
|
||||
* <p>
|
||||
* This reflects the fact that a binding for ComponentProvider also can function as a provider for Controller
|
||||
* and RestController; and that RestController in turn can be interpreted as a specialized Component or Controller.
|
||||
* Therefore we would expect in a situation where a concrete annotation of type RestController is found in the AST,
|
||||
* a symbol provider for Components should be asked to produce symbols for Component, Controller and RestController,
|
||||
* so should result in 3 separate calls to the symbols provider.
|
||||
*/
|
||||
public Collection<T> get(ITypeBinding annotationType) {
|
||||
ImmutableList.Builder<T> found = ImmutableList.builder();
|
||||
findElements(annotationType, new LinkedHashSet<>(), found::add);
|
||||
return found.build();
|
||||
}
|
||||
|
||||
public Collection<T> getAll() {
|
||||
ImmutableList.Builder<T> found = ImmutableList.builder();
|
||||
Collection<Binding<T>> values = bindings.values();
|
||||
values.forEach(binding -> found.add(binding.value));
|
||||
return found.build();
|
||||
}
|
||||
|
||||
private void findElements(ITypeBinding typeBinding, HashSet<String> seen, Consumer<T> requestor) {
|
||||
String qname = typeBinding.getQualifiedName();
|
||||
if (seen.add(qname)) {
|
||||
Binding<T> binding = bindings.get(qname);
|
||||
boolean isOverriding = false;
|
||||
if (binding!=null) {
|
||||
requestor.accept(binding.value);
|
||||
isOverriding = binding.isOverriding;
|
||||
}
|
||||
if (!isOverriding) {
|
||||
for (ITypeBinding superAnnotation : AnnotationHierarchies.getDirectSuperAnnotations(typeBinding)) {
|
||||
findElements(superAnnotation, seen, requestor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void put(String annotationName, T value) {
|
||||
put(annotationName, true, value);
|
||||
}
|
||||
|
||||
public boolean containsKey(String fqName) {
|
||||
return bindings.containsKey(fqName);
|
||||
}
|
||||
|
||||
// private static int indent = 0;
|
||||
//
|
||||
// private static void debug(int indent, String msg) {
|
||||
// for (int i = 0; i < indent; i++) {
|
||||
// System.out.print(" ");
|
||||
// }
|
||||
// System.out.println(msg);
|
||||
// }
|
||||
// private static int debug_in(String msg) {
|
||||
// for (int i = 0; i < indent; i++) {
|
||||
// System.out.print(" ");
|
||||
// }
|
||||
// System.out.println(">> "+msg);
|
||||
// return indent ++;
|
||||
// }
|
||||
// private static void debug_out(String msg) {
|
||||
// indent --;
|
||||
// for (int i = 0; i < indent; i++) {
|
||||
// System.out.print(" ");
|
||||
// }
|
||||
// System.out.println("<< "+msg);
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -1,178 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2018 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.autowired;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.TypeDeclaration;
|
||||
import org.eclipse.lsp4j.Hover;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.eclipse.lsp4j.jsonrpc.messages.Either;
|
||||
import org.springframework.ide.vscode.boot.java.Annotations;
|
||||
import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchies;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.HoverProvider;
|
||||
import org.springframework.ide.vscode.boot.java.livehover.ComponentInjectionsHoverProvider;
|
||||
import org.springframework.ide.vscode.boot.java.livehover.LiveHoverUtils;
|
||||
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBeansModel;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
import org.springframework.ide.vscode.commons.util.StringUtil;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class AutowiredHoverProvider implements HoverProvider {
|
||||
|
||||
@Override
|
||||
public Collection<Range> getLiveHoverHints(Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) {
|
||||
try {
|
||||
LiveBean definedBean = getDefinedBean(annotation);
|
||||
if (definedBean != null) {
|
||||
for (SpringBootApp app : runningApps) {
|
||||
try {
|
||||
List<LiveBean> relevantBeans = LiveHoverUtils.findRelevantBeans(app, definedBean).collect(Collectors.toList());
|
||||
|
||||
if (!relevantBeans.isEmpty()) {
|
||||
for (LiveBean bean : relevantBeans) {
|
||||
String[] dependencies = bean.getDependencies();
|
||||
if (dependencies != null && dependencies.length > 0) {
|
||||
Range hoverRange = doc.toRange(annotation.getStartPosition(), annotation.getLength());
|
||||
return ImmutableList.of(hoverRange);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
Log.log(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
Log.log(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Hover provideHover(ASTNode node, Annotation annotation, ITypeBinding type, int offset,
|
||||
TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) {
|
||||
if (runningApps.length > 0) {
|
||||
|
||||
StringBuilder hover = new StringBuilder();
|
||||
|
||||
LiveBean definedBean = getDefinedBean(annotation);
|
||||
if (definedBean != null) {
|
||||
|
||||
hover.append("**Injection report for " + LiveHoverUtils.showBean(definedBean) + "**\n\n");
|
||||
|
||||
boolean hasInterestingApp = false;
|
||||
boolean hasAutowiring = false;
|
||||
|
||||
for (SpringBootApp app : runningApps) {
|
||||
LiveBeansModel beans = app.getBeans();
|
||||
List<LiveBean> relevantBeans = LiveHoverUtils.findRelevantBeans(app, definedBean).collect(Collectors.toList());
|
||||
|
||||
if (!relevantBeans.isEmpty()) {
|
||||
if (!hasInterestingApp) {
|
||||
hasInterestingApp = true;
|
||||
} else {
|
||||
hover.append("\n\n");
|
||||
}
|
||||
hover.append(LiveHoverUtils.niceAppName(app) + ":");
|
||||
|
||||
for (LiveBean bean : relevantBeans) {
|
||||
hover.append("\n\n");
|
||||
hasAutowiring |= addAutomaticallyWired(hover, annotation, beans, bean, project);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasInterestingApp && hasAutowiring) {
|
||||
return new Hover(ImmutableList.of(Either.forLeft(hover.toString())));
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private LiveBean getDefinedBean(Annotation autowiredAnnotation) {
|
||||
TypeDeclaration declaringType = ASTUtils.findDeclaringType(autowiredAnnotation);
|
||||
if (declaringType != null) {
|
||||
for (Annotation annotation : ASTUtils.getAnnotations(declaringType)) {
|
||||
if (AnnotationHierarchies.isSubtypeOf(annotation, Annotations.COMPONENT)) {
|
||||
return ComponentInjectionsHoverProvider.getDefinedBeanForComponent(annotation);
|
||||
}
|
||||
}
|
||||
//TODO: handler below is an attempt to do something that may work in many cases, but is probably
|
||||
// missing logics for special cases where annotation attributes on the declaring type matter.
|
||||
ITypeBinding beanType = declaringType.resolveBinding();
|
||||
if (beanType!=null) {
|
||||
String beanTypeName = beanType.getName();
|
||||
if (StringUtil.hasText(beanTypeName)) {
|
||||
return LiveBean.builder()
|
||||
.id(Character.toLowerCase(beanTypeName.charAt(0)) + beanTypeName.substring(1))
|
||||
.type(beanTypeName)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean addAutomaticallyWired(StringBuilder hover, Annotation annotation, LiveBeansModel beans, LiveBean bean, IJavaProject project) {
|
||||
boolean result = false;
|
||||
String[] dependencies = bean.getDependencies();
|
||||
|
||||
if (dependencies != null && dependencies.length > 0) {
|
||||
result = true;
|
||||
hover.append(LiveHoverUtils.showBean(bean) + " got autowired with:\n\n");
|
||||
|
||||
boolean firstDependency = true;
|
||||
for (String injectedBean : dependencies) {
|
||||
if (!firstDependency) {
|
||||
hover.append("\n");
|
||||
}
|
||||
List<LiveBean> dependencyBeans = beans.getBeansOfName(injectedBean);
|
||||
for (LiveBean dependencyBean : dependencyBeans) {
|
||||
hover.append("- " + LiveHoverUtils.showBeanWithResource(dependencyBean, " ", project));
|
||||
}
|
||||
firstDependency = false;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Hover provideHover(ASTNode node, TypeDeclaration typeDeclaration, ITypeBinding type, int offset,
|
||||
TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Range> getLiveHoverHints(TypeDeclaration typeDeclaration, TextDocument doc,
|
||||
SpringBootApp[] runningApps) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,169 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2018 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.beans;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.MethodDeclaration;
|
||||
import org.eclipse.jdt.core.dom.ParameterizedType;
|
||||
import org.eclipse.jdt.core.dom.StringLiteral;
|
||||
import org.eclipse.jdt.core.dom.Type;
|
||||
import org.eclipse.jdt.core.dom.TypeDeclaration;
|
||||
import org.eclipse.lsp4j.Location;
|
||||
import org.eclipse.lsp4j.SymbolInformation;
|
||||
import org.eclipse.lsp4j.SymbolKind;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider;
|
||||
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
|
||||
import org.springframework.ide.vscode.boot.java.utils.FunctionUtils;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion;
|
||||
import org.springframework.ide.vscode.commons.util.BadLocationException;
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import reactor.util.function.Tuple2;
|
||||
import reactor.util.function.Tuple3;
|
||||
import reactor.util.function.Tuples;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
public class BeansSymbolProvider implements SymbolProvider {
|
||||
|
||||
private static final String[] NAME_ATTRIBUTES = {"value", "name"};
|
||||
|
||||
@Override
|
||||
public Collection<SymbolInformation> getSymbols(Annotation node, ITypeBinding annotationType, Collection<ITypeBinding> metaAnnotations, TextDocument doc) {
|
||||
boolean isFunction = isFunctionBean(node);
|
||||
|
||||
ImmutableList.Builder<SymbolInformation> symbols = ImmutableList.builder();
|
||||
String beanType = getBeanType(node);
|
||||
for (Tuple2<String, DocumentRegion> nameAndRegion : getBeanNames(node, doc)) {
|
||||
try {
|
||||
symbols.add(new SymbolInformation(
|
||||
beanLabel(isFunction, nameAndRegion.getT1(), beanType, "@Bean"),
|
||||
SymbolKind.Interface,
|
||||
new Location(doc.getUri(), doc.toRange(nameAndRegion.getT2()))
|
||||
));
|
||||
} catch (BadLocationException e) {
|
||||
Log.log(e);
|
||||
}
|
||||
}
|
||||
return symbols.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<SymbolInformation> getSymbols(TypeDeclaration typeDeclaration, TextDocument doc) {
|
||||
// this checks function beans that are defined as implementations of Function interfaces
|
||||
Tuple3<String, String, DocumentRegion> functionBean = FunctionUtils.getFunctionBean(typeDeclaration, doc);
|
||||
if (functionBean != null) {
|
||||
try {
|
||||
SymbolInformation symbol = new SymbolInformation(
|
||||
beanLabel(true, functionBean.getT1(), functionBean.getT2(), null),
|
||||
SymbolKind.Interface,
|
||||
new Location(doc.getUri(), doc.toRange(functionBean.getT3())));
|
||||
return ImmutableList.of(symbol);
|
||||
} catch (BadLocationException e) {
|
||||
Log.log(e);
|
||||
}
|
||||
}
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
protected Collection<Tuple2<String, DocumentRegion>> getBeanNames(Annotation node, TextDocument doc) {
|
||||
Collection<StringLiteral> beanNameNodes = getBeanNameLiterals(node);
|
||||
|
||||
if (beanNameNodes != null && !beanNameNodes.isEmpty()) {
|
||||
ImmutableList.Builder<Tuple2<String,DocumentRegion>> namesAndRegions = ImmutableList.builder();
|
||||
for (StringLiteral nameNode : beanNameNodes) {
|
||||
String name = ASTUtils.getLiteralValue(nameNode);
|
||||
namesAndRegions.add(Tuples.of(name, ASTUtils.stringRegion(doc, nameNode)));
|
||||
}
|
||||
return namesAndRegions.build();
|
||||
}
|
||||
else {
|
||||
ASTNode parent = node.getParent();
|
||||
if (parent instanceof MethodDeclaration) {
|
||||
MethodDeclaration method = (MethodDeclaration) parent;
|
||||
return ImmutableList.of(Tuples.of(
|
||||
method.getName().toString(),
|
||||
ASTUtils.nameRegion(doc, node)
|
||||
));
|
||||
}
|
||||
return ImmutableList.of();
|
||||
}
|
||||
}
|
||||
|
||||
protected String beanLabel(boolean isFunctionBean, String beanName, String beanType, String markerString) {
|
||||
StringBuilder symbolLabel = new StringBuilder();
|
||||
symbolLabel.append('@');
|
||||
symbolLabel.append(isFunctionBean ? '>' : '+');
|
||||
symbolLabel.append(' ');
|
||||
symbolLabel.append('\'');
|
||||
symbolLabel.append(beanName);
|
||||
symbolLabel.append('\'');
|
||||
|
||||
markerString = markerString != null && markerString.length() > 0 ? " (" + markerString + ") " : " ";
|
||||
symbolLabel.append(markerString);
|
||||
|
||||
symbolLabel.append(beanType);
|
||||
return symbolLabel.toString();
|
||||
}
|
||||
|
||||
protected Collection<StringLiteral> getBeanNameLiterals(Annotation node) {
|
||||
ImmutableList.Builder<StringLiteral> literals = ImmutableList.builder();
|
||||
for (String attrib : NAME_ATTRIBUTES) {
|
||||
ASTUtils.getAttribute(node, attrib).ifPresent((valueExp) -> {
|
||||
literals.addAll(ASTUtils.getExpressionValueAsListOfLiterals(valueExp));
|
||||
});
|
||||
}
|
||||
return literals.build();
|
||||
}
|
||||
|
||||
protected String getBeanType(Annotation node) {
|
||||
ASTNode parent = node.getParent();
|
||||
if (parent instanceof MethodDeclaration) {
|
||||
MethodDeclaration method = (MethodDeclaration) parent;
|
||||
String returnType = method.getReturnType2().resolveBinding().getName();
|
||||
return returnType;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isFunctionBean(Annotation node) {
|
||||
ASTNode parent = node.getParent();
|
||||
if (parent instanceof MethodDeclaration) {
|
||||
MethodDeclaration method = (MethodDeclaration) parent;
|
||||
|
||||
String returnType = null;
|
||||
if (method.getReturnType2().isParameterizedType()) {
|
||||
ParameterizedType paramType = (ParameterizedType) method.getReturnType2();
|
||||
Type type = paramType.getType();
|
||||
ITypeBinding typeBinding = type.resolveBinding();
|
||||
returnType = typeBinding.getBinaryName();
|
||||
}
|
||||
else {
|
||||
returnType = method.getReturnType2().resolveBinding().getQualifiedName();
|
||||
}
|
||||
|
||||
return FunctionUtils.FUNCTION_FUNCTION_TYPE.equals(returnType) || FunctionUtils.FUNCTION_CONSUMER_TYPE.equals(returnType)
|
||||
|| FunctionUtils.FUNCTION_SUPPLIER_TYPE.equals(returnType);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2018 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.beans;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.TypeDeclaration;
|
||||
import org.eclipse.lsp4j.Location;
|
||||
import org.eclipse.lsp4j.SymbolInformation;
|
||||
import org.eclipse.lsp4j.SymbolKind;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider;
|
||||
import org.springframework.ide.vscode.commons.util.BadLocationException;
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
public class ComponentSymbolProvider implements SymbolProvider {
|
||||
|
||||
@Override
|
||||
public Collection<SymbolInformation> getSymbols(Annotation node, ITypeBinding annotationType, Collection<ITypeBinding> metaAnnotations, TextDocument doc) {
|
||||
try {
|
||||
return ImmutableList.of(
|
||||
createSymbol(node, annotationType, metaAnnotations, doc)
|
||||
);
|
||||
}
|
||||
catch (Exception e) {
|
||||
Log.log(e);
|
||||
}
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
protected SymbolInformation createSymbol(Annotation node, ITypeBinding annotationType, Collection<ITypeBinding> metaAnnotations, TextDocument doc) throws BadLocationException {
|
||||
String annotationTypeName = annotationType.getName();
|
||||
Collection<String> metaAnnotationNames = metaAnnotations.stream()
|
||||
.map(ITypeBinding::getName)
|
||||
.collect(Collectors.toList());
|
||||
String beanName = getBeanName(node);
|
||||
String beanType = getBeanType(node);
|
||||
|
||||
SymbolInformation symbol = new SymbolInformation(
|
||||
beanLabel("+", annotationTypeName, metaAnnotationNames, beanName, beanType), SymbolKind.Interface,
|
||||
new Location(doc.getUri(), doc.toRange(node.getStartPosition(), node.getLength())));
|
||||
return symbol;
|
||||
}
|
||||
|
||||
protected String beanLabel(String searchPrefix, String annotationTypeName, Collection<String> metaAnnotationNames, String beanName, String beanType) {
|
||||
StringBuilder symbolLabel = new StringBuilder();
|
||||
symbolLabel.append("@");
|
||||
symbolLabel.append(searchPrefix);
|
||||
symbolLabel.append(' ');
|
||||
symbolLabel.append('\'');
|
||||
symbolLabel.append(beanName);
|
||||
symbolLabel.append('\'');
|
||||
symbolLabel.append(" (@");
|
||||
symbolLabel.append(annotationTypeName);
|
||||
if (!metaAnnotationNames.isEmpty()) {
|
||||
symbolLabel.append(" <: ");
|
||||
boolean first = true;
|
||||
for (String ma : metaAnnotationNames) {
|
||||
if (!first) {
|
||||
symbolLabel.append(", ");
|
||||
}
|
||||
symbolLabel.append("@");
|
||||
symbolLabel.append(ma);
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
symbolLabel.append(") ");
|
||||
symbolLabel.append(beanType);
|
||||
return symbolLabel.toString();
|
||||
}
|
||||
|
||||
private String getBeanName(Annotation node) {
|
||||
ASTNode parent = node.getParent();
|
||||
if (parent instanceof TypeDeclaration) {
|
||||
TypeDeclaration type = (TypeDeclaration) parent;
|
||||
|
||||
String beanName = type.getName().toString();
|
||||
if (beanName.length() > 0 && Character.isUpperCase(beanName.charAt(0))) {
|
||||
beanName = Character.toLowerCase(beanName.charAt(0)) + beanName.substring(1);
|
||||
}
|
||||
return beanName;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getBeanType(Annotation node) {
|
||||
ASTNode parent = node.getParent();
|
||||
if (parent instanceof TypeDeclaration) {
|
||||
TypeDeclaration type = (TypeDeclaration) parent;
|
||||
String returnType = type.resolveBinding().getName();
|
||||
return returnType;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<SymbolInformation> getSymbols(TypeDeclaration typeDeclaration, TextDocument doc) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,170 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2018 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.conditionals;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.IMethodBinding;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.MethodDeclaration;
|
||||
import org.eclipse.jdt.core.dom.TypeDeclaration;
|
||||
import org.eclipse.lsp4j.Hover;
|
||||
import org.eclipse.lsp4j.MarkedString;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.eclipse.lsp4j.jsonrpc.messages.Either;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.HoverProvider;
|
||||
import org.springframework.ide.vscode.boot.java.livehover.LiveHoverUtils;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.LiveConditional;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
/**
|
||||
*
|
||||
* Provides live hovers and hints for @ConditionalOn... Spring Boot annotations
|
||||
* from running spring boot apps.
|
||||
*/
|
||||
public class ConditionalsLiveHoverProvider implements HoverProvider {
|
||||
|
||||
@Override
|
||||
public Hover provideHover(ASTNode node, Annotation annotation, ITypeBinding type, int offset,
|
||||
TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) {
|
||||
return provideHover(annotation, doc, runningApps);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Range> getLiveHoverHints(Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) {
|
||||
try {
|
||||
Optional<List<LiveConditional>> val = getMatchedLiveConditionals(annotation, runningApps);
|
||||
if (val.isPresent()) {
|
||||
Range hoverRange = doc.toRange(annotation.getStartPosition(), annotation.getLength());
|
||||
return ImmutableList.of(hoverRange);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Optional<List<LiveConditional>> getMatchedLiveConditionals(Annotation annotation,
|
||||
SpringBootApp[] runningApps) throws Exception {
|
||||
if (runningApps != null) {
|
||||
List<LiveConditional> all = new ArrayList<>();
|
||||
|
||||
for (SpringBootApp springBootApp : runningApps) {
|
||||
springBootApp.getLiveConditionals().ifPresent((conditionals) -> {
|
||||
conditionals.stream().forEach((conditional) -> {
|
||||
if (matchesAnnotation(annotation, conditional)) {
|
||||
all.add(conditional);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
if (!all.isEmpty()) {
|
||||
return Optional.of(all);
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private Hover provideHover(Annotation annotation, TextDocument doc,
|
||||
SpringBootApp[] runningApps) {
|
||||
|
||||
try {
|
||||
List<Either<String, MarkedString>> hoverContent = new ArrayList<>();
|
||||
Optional<List<LiveConditional>> val = getMatchedLiveConditionals(annotation, runningApps);
|
||||
|
||||
if (val.isPresent()) {
|
||||
addHoverContent(val.get(), hoverContent);
|
||||
}
|
||||
|
||||
Range hoverRange = doc.toRange(annotation.getStartPosition(), annotation.getLength());
|
||||
Hover hover = new Hover();
|
||||
|
||||
hover.setContents(hoverContent);
|
||||
hover.setRange(hoverRange);
|
||||
|
||||
return hover;
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void addHoverContent(List<LiveConditional> conditionals, List<Either<String, MarkedString>> hoverContent)
|
||||
throws Exception {
|
||||
for (int i = 0; i < conditionals.size(); i++) {
|
||||
LiveConditional conditional = conditionals.get(i);
|
||||
hoverContent.add(Either.forLeft(conditional.getMessage()));
|
||||
hoverContent.add(Either.forLeft(LiveHoverUtils.niceAppName(conditional.getProcessId(), conditional.getProcessName())));
|
||||
|
||||
if (i < conditionals.size() - 1) {
|
||||
hoverContent.add(Either.forLeft("---"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param annotation
|
||||
* @param jsonKey
|
||||
* @return true if the annotation matches the information in the json key from
|
||||
* the running app.
|
||||
*/
|
||||
protected boolean matchesAnnotation(Annotation annotation, LiveConditional liveConditional) {
|
||||
|
||||
// First check that the annotation matches the live conditional annotation
|
||||
String annotationName = annotation.resolveTypeBinding().getName();
|
||||
if (!liveConditional.getMessage().contains(annotationName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that Java type in annotation in editor matches Java information in the live Conditional
|
||||
ASTNode parent = annotation.getParent();
|
||||
String typeInfo = liveConditional.getTypeInfo();
|
||||
|
||||
if (parent instanceof MethodDeclaration) {
|
||||
MethodDeclaration methodDec = (MethodDeclaration) parent;
|
||||
IMethodBinding binding = methodDec.resolveBinding();
|
||||
String annotationDeclaringClassName = binding.getDeclaringClass().getName();
|
||||
String annotationMethodName = binding.getName();
|
||||
return typeInfo.contains(annotationDeclaringClassName) && typeInfo.contains(annotationMethodName);
|
||||
} else if (parent instanceof TypeDeclaration) {
|
||||
TypeDeclaration typeDec = (TypeDeclaration) parent;
|
||||
String annotationDeclaringClassName = typeDec.resolveBinding().getName();
|
||||
return typeInfo.contains(annotationDeclaringClassName);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Hover provideHover(ASTNode node, TypeDeclaration typeDeclaration, ITypeBinding type, int offset,
|
||||
TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Range> getLiveHoverHints(TypeDeclaration typeDeclaration, TextDocument doc,
|
||||
SpringBootApp[] runningApps) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.handlers;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.eclipse.lsp4j.CodeLens;
|
||||
import org.eclipse.lsp4j.CodeLensParams;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class BootJavaCodeLensEngine {
|
||||
|
||||
private final SimpleLanguageServer server;
|
||||
// private final JavaProjectFinder projectFinder;
|
||||
|
||||
public BootJavaCodeLensEngine(SimpleLanguageServer server, JavaProjectFinder projectFinder) {
|
||||
this.server = server;
|
||||
// this.projectFinder = projectFinder;
|
||||
}
|
||||
|
||||
public CompletableFuture<List<? extends CodeLens>> createCodeLenses(CodeLensParams params) {
|
||||
SimpleTextDocumentService documents = server.getTextDocumentService();
|
||||
String docURI = params.getTextDocument().getUri();
|
||||
|
||||
if (documents.get(docURI) != null) {
|
||||
TextDocument doc = documents.get(docURI).copy();
|
||||
try {
|
||||
CompletableFuture<List<? extends CodeLens>> codeLensesResult = provideCodeLenses(doc);
|
||||
if (codeLensesResult != null) {
|
||||
return codeLensesResult;
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
return SimpleTextDocumentService.NO_CODELENS;
|
||||
}
|
||||
|
||||
private CompletableFuture<List<? extends CodeLens>> provideCodeLenses(TextDocument doc) {
|
||||
List<CodeLens> result = new ArrayList<>();
|
||||
/**
|
||||
CodeLens codeLens = new CodeLens();
|
||||
|
||||
Range range = new Range();
|
||||
range.setStart(new Position(5, 0));
|
||||
range.setEnd(new Position(5, 10));
|
||||
codeLens.setRange(range);
|
||||
|
||||
Command command = new Command("my first awesome code lens", "my first code lens command");
|
||||
codeLens.setCommand(command);
|
||||
|
||||
codeLens.setData("some data");
|
||||
|
||||
result.add(codeLens); */
|
||||
return CompletableFuture.completedFuture(result);
|
||||
}
|
||||
|
||||
public CompletableFuture<CodeLens> resolveCodeLens(CodeLens unresolved) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2018 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.handlers;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.NodeFinder;
|
||||
import org.springframework.ide.vscode.boot.java.BootJavaLanguageServer;
|
||||
import org.springframework.ide.vscode.boot.java.snippets.JavaSnippetManager;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionEngine;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class BootJavaCompletionEngine implements ICompletionEngine {
|
||||
|
||||
private Map<String, CompletionProvider> completionProviders;
|
||||
private JavaSnippetManager snippets;
|
||||
private BootJavaLanguageServer server;
|
||||
|
||||
public BootJavaCompletionEngine(BootJavaLanguageServer server, Map<String, CompletionProvider> specificProviders, JavaSnippetManager snippets) {
|
||||
this.server = server;
|
||||
this.completionProviders = specificProviders;
|
||||
this.snippets = snippets;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ICompletionProposal> getCompletions(TextDocument document, int offset) throws Exception {
|
||||
return server.getCompilationUnitCache().withCompilationUnit(document, cu -> {
|
||||
if (cu != null) {
|
||||
ASTNode node = NodeFinder.perform(cu, offset, 0);
|
||||
|
||||
if (node != null) {
|
||||
Collection<ICompletionProposal> completions = new ArrayList<ICompletionProposal>();
|
||||
completions.addAll(collectCompletionsForAnnotations(node, offset, document));
|
||||
completions.addAll(snippets.getCompletions(document, offset, node, cu));
|
||||
return completions;
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
});
|
||||
}
|
||||
|
||||
private Collection<ICompletionProposal> collectCompletionsForAnnotations(ASTNode node, int offset, IDocument doc) {
|
||||
Annotation annotation = null;
|
||||
ASTNode exactNode = node;
|
||||
|
||||
while (node != null && !(node instanceof Annotation)) {
|
||||
node = node.getParent();
|
||||
}
|
||||
|
||||
if (node != null) {
|
||||
annotation = (Annotation) node;
|
||||
ITypeBinding type = annotation.resolveTypeBinding();
|
||||
if (type != null) {
|
||||
String qualifiedName = type.getQualifiedName();
|
||||
if (qualifiedName != null) {
|
||||
CompletionProvider provider = this.completionProviders.get(qualifiedName);
|
||||
if (provider != null) {
|
||||
return provider.provideCompletions(exactNode, annotation, type, offset, doc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.handlers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.lsp4j.DocumentSymbolParams;
|
||||
import org.eclipse.lsp4j.SymbolInformation;
|
||||
import org.springframework.ide.vscode.boot.java.utils.SpringIndexer;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.DocumentSymbolHandler;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class BootJavaDocumentSymbolHandler implements DocumentSymbolHandler {
|
||||
|
||||
private SpringIndexer indexer;
|
||||
|
||||
public BootJavaDocumentSymbolHandler(SpringIndexer indexer) {
|
||||
this.indexer = indexer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends SymbolInformation> handle(DocumentSymbolParams params) {
|
||||
return indexer.getSymbols(params.getTextDocument().getUri());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,294 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2018 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.handlers;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.ASTVisitor;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.MarkerAnnotation;
|
||||
import org.eclipse.jdt.core.dom.NodeFinder;
|
||||
import org.eclipse.jdt.core.dom.NormalAnnotation;
|
||||
import org.eclipse.jdt.core.dom.SimpleName;
|
||||
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
|
||||
import org.eclipse.jdt.core.dom.TypeDeclaration;
|
||||
import org.eclipse.lsp4j.Hover;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
||||
import org.eclipse.lsp4j.TextDocumentPositionParams;
|
||||
import org.eclipse.lsp4j.jsonrpc.messages.Either;
|
||||
import org.springframework.ide.vscode.boot.java.BootJavaLanguageServer;
|
||||
import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchyAwareLookup;
|
||||
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
|
||||
import org.springframework.ide.vscode.commons.java.IClasspath;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.HoverHandler;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService;
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class BootJavaHoverProvider implements HoverHandler {
|
||||
|
||||
private JavaProjectFinder projectFinder;
|
||||
private BootJavaLanguageServer server;
|
||||
private AnnotationHierarchyAwareLookup<HoverProvider> hoverProviders;
|
||||
private RunningAppProvider runningAppProvider;
|
||||
|
||||
public BootJavaHoverProvider(BootJavaLanguageServer server, JavaProjectFinder projectFinder, AnnotationHierarchyAwareLookup<HoverProvider> specificProviders, RunningAppProvider runningAppProvider) {
|
||||
this.server = server;
|
||||
this.projectFinder = projectFinder;
|
||||
this.hoverProviders = specificProviders;
|
||||
this.runningAppProvider = runningAppProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Hover> handle(TextDocumentPositionParams params) {
|
||||
SimpleTextDocumentService documents = server.getTextDocumentService();
|
||||
if (documents.get(params) != null) {
|
||||
TextDocument doc = documents.get(params).copy();
|
||||
try {
|
||||
int offset = doc.toOffset(params.getPosition());
|
||||
Hover hoverResult = provideHover(doc, offset);
|
||||
if (hoverResult != null) {
|
||||
return CompletableFuture.completedFuture(hoverResult);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
return SimpleTextDocumentService.NO_HOVER;
|
||||
}
|
||||
|
||||
public Range[] getLiveHoverHints(final TextDocument document, final SpringBootApp[] runningBootApps) {
|
||||
return server.getCompilationUnitCache().withCompilationUnit(document, cu -> {
|
||||
Collection<Range> result = new HashSet<>();
|
||||
try {
|
||||
if (cu != null) {
|
||||
cu.accept(new ASTVisitor() {
|
||||
|
||||
@Override
|
||||
public boolean visit(TypeDeclaration node) {
|
||||
try {
|
||||
extractLiveHintsForType(node, document, runningBootApps, result);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return super.visit(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(SingleMemberAnnotation node) {
|
||||
try {
|
||||
extractLiveHintsForAnnotation(node, document, runningBootApps, result);
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
}
|
||||
|
||||
return super.visit(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(NormalAnnotation node) {
|
||||
try {
|
||||
extractLiveHintsForAnnotation(node, document, runningBootApps, result);
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
}
|
||||
|
||||
return super.visit(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(MarkerAnnotation node) {
|
||||
try {
|
||||
extractLiveHintsForAnnotation(node, document, runningBootApps, result);
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
}
|
||||
|
||||
return super.visit(node);
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
}
|
||||
return result.toArray(new Range[result.size()]);
|
||||
});
|
||||
}
|
||||
|
||||
protected void extractLiveHintsForType(TypeDeclaration typeDeclaration, TextDocument doc, SpringBootApp[] runningApps, Collection<Range> result) {
|
||||
Collection<HoverProvider> providers = this.hoverProviders.getAll();
|
||||
if (!providers.isEmpty()) {
|
||||
for (HoverProvider provider : providers) {
|
||||
getProject(doc).ifPresent(project -> {
|
||||
if (hasActuatorDependency(project)) {
|
||||
Collection<Range> hints = provider.getLiveHoverHints(typeDeclaration, doc, runningApps);
|
||||
if (hints!=null) {
|
||||
result.addAll(hints);
|
||||
}
|
||||
} else {
|
||||
//Do nothing... we don't want a highlight for the 'no actuator warning'
|
||||
//ASTUtils.nameRange(doc, annotation).ifPresent(result::add);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void extractLiveHintsForAnnotation(Annotation annotation, TextDocument doc, SpringBootApp[] runningApps, Collection<Range> result) {
|
||||
ITypeBinding type = annotation.resolveTypeBinding();
|
||||
if (type != null) {
|
||||
if (runningApps.length > 0) {
|
||||
for (HoverProvider provider : this.hoverProviders.get(type)) {
|
||||
getProject(doc).ifPresent(project -> {
|
||||
if (hasActuatorDependency(project)) {
|
||||
Collection<Range> hints = provider.getLiveHoverHints(annotation, doc, runningApps);
|
||||
if (hints!=null) {
|
||||
result.addAll(hints);
|
||||
}
|
||||
} else {
|
||||
//Do nothing... we don't want a highlight for the 'no actuator warning'
|
||||
//ASTUtils.nameRange(doc, annotation).ifPresent(result::add);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Hover provideHover(TextDocument document, int offset) throws Exception {
|
||||
IJavaProject project = getProject(document).orElse(null);
|
||||
if (project!=null) {
|
||||
return server.getCompilationUnitCache().withCompilationUnit(document, cu -> {
|
||||
ASTNode node = NodeFinder.perform(cu, offset, 0);
|
||||
if (node != null) {
|
||||
return provideHover(node, offset, document, project);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Hover provideHover(ASTNode node, int offset, TextDocument doc, IJavaProject project) {
|
||||
|
||||
// look for spring annotations first
|
||||
ASTNode annotationNode = node;
|
||||
while (annotationNode != null && !(annotationNode instanceof Annotation)) {
|
||||
annotationNode = annotationNode.getParent();
|
||||
}
|
||||
if (annotationNode != null) {
|
||||
return provideHoverForAnnotation(node, (Annotation) annotationNode, offset, doc, project);
|
||||
}
|
||||
|
||||
// then do additional AST node coverage
|
||||
if (node instanceof SimpleName && node.getParent() instanceof TypeDeclaration) {
|
||||
return provideHoverForTypeDeclaration(node, (TypeDeclaration) node.getParent(), offset, doc, project);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Hover provideHoverForAnnotation(ASTNode exactNode, Annotation annotation, int offset, TextDocument doc, IJavaProject project) {
|
||||
ITypeBinding type = annotation.resolveTypeBinding();
|
||||
if (type != null) {
|
||||
SpringBootApp[] runningApps = getRunningSpringApps(project);
|
||||
if (runningApps.length > 0) {
|
||||
for (HoverProvider provider : this.hoverProviders.get(type)) {
|
||||
Hover hover = provider.provideHover(exactNode, annotation, type, offset, doc, project, runningApps);
|
||||
if (hover!=null) {
|
||||
//TODO: compose multiple hovers somehow instead of just returning the first one?
|
||||
return hover;
|
||||
}
|
||||
}
|
||||
//Only reaching here if we didn't get a hover.
|
||||
if (!hasActuatorDependency(project)) {
|
||||
DocumentRegion region = ASTUtils.nameRegion(doc, annotation);
|
||||
if (region.containsOffset(offset)) {
|
||||
return actuatorWarning(project);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Hover provideHoverForTypeDeclaration(ASTNode exactNode, TypeDeclaration typeDeclaration, int offset, TextDocument doc, IJavaProject project) {
|
||||
SpringBootApp[] runningApps = getRunningSpringApps(project);
|
||||
if (runningApps.length > 0) {
|
||||
ITypeBinding type = typeDeclaration.resolveBinding();
|
||||
|
||||
for (HoverProvider provider : this.hoverProviders.getAll()) {
|
||||
Hover hover = provider.provideHover(exactNode, typeDeclaration, type, offset, doc, project, runningApps);
|
||||
if (hover!=null) {
|
||||
//TODO: compose multiple hovers somehow instead of just returning the first one?
|
||||
return hover;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Hover actuatorWarning(IJavaProject project) {
|
||||
String hoverText =
|
||||
"**No live hover information available**.\n"+
|
||||
"\n" +
|
||||
"Live hover providers use various `spring-boot-actuator` endpoints to retrieve information. "+
|
||||
"Consider adding `spring-boot-actuator` as a dependency to your project `"+project.getElementName()+"`";
|
||||
return new Hover(ImmutableList.of(Either.forLeft(hoverText)));
|
||||
}
|
||||
|
||||
private boolean hasActuatorDependency(IJavaProject project) {
|
||||
try {
|
||||
IClasspath classpath = project.getClasspath();
|
||||
if (classpath!=null) {
|
||||
return classpath.getClasspathEntries().stream().anyMatch(cpe -> {
|
||||
String name = cpe.getFileName().toString();
|
||||
return name.startsWith("spring-boot-actuator-");
|
||||
});
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Optional<IJavaProject> getProject(IDocument doc) {
|
||||
return this.projectFinder.find(new TextDocumentIdentifier(doc.getUri()));
|
||||
}
|
||||
|
||||
private SpringBootApp[] getRunningSpringApps(IJavaProject project) {
|
||||
try {
|
||||
return runningAppProvider.getAllRunningSpringApps().toArray(new SpringBootApp[0]);
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
return new SpringBootApp[0];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016-2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.handlers;
|
||||
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IReconcileEngine;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class BootJavaReconcileEngine implements IReconcileEngine {
|
||||
|
||||
@Override
|
||||
public void reconcile(IDocument doc, IProblemCollector problemCollector) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.handlers;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.core.JavaCore;
|
||||
import org.eclipse.jdt.core.dom.AST;
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.ASTParser;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.CompilationUnit;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.NodeFinder;
|
||||
import org.eclipse.lsp4j.Location;
|
||||
import org.eclipse.lsp4j.ReferenceParams;
|
||||
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
||||
import org.springframework.ide.vscode.commons.java.IClasspath;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.ReferencesHandler;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class BootJavaReferencesHandler implements ReferencesHandler {
|
||||
|
||||
private JavaProjectFinder projectFinder;
|
||||
private SimpleLanguageServer server;
|
||||
private Map<String, ReferenceProvider> referenceProviders;
|
||||
|
||||
public BootJavaReferencesHandler(SimpleLanguageServer server, JavaProjectFinder projectFinder, Map<String, ReferenceProvider> specificProviders) {
|
||||
this.server = server;
|
||||
this.projectFinder = projectFinder;
|
||||
this.referenceProviders = specificProviders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<? extends Location>> handle(ReferenceParams params) {
|
||||
SimpleTextDocumentService documents = server.getTextDocumentService();
|
||||
TextDocument doc = documents.get(params).copy();
|
||||
if (doc != null) {
|
||||
try {
|
||||
int offset = doc.toOffset(params.getPosition());
|
||||
CompletableFuture<List<? extends Location>> referencesResult = provideReferences(doc, offset);
|
||||
if (referencesResult != null) {
|
||||
return referencesResult;
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
return SimpleTextDocumentService.NO_REFERENCES;
|
||||
}
|
||||
|
||||
private CompletableFuture<List<? extends Location>> provideReferences(TextDocument document, int offset) throws Exception {
|
||||
ASTParser parser = ASTParser.newParser(AST.JLS9);
|
||||
Map<String, String> options = JavaCore.getOptions();
|
||||
JavaCore.setComplianceOptions(JavaCore.VERSION_1_8, options);
|
||||
parser.setCompilerOptions(options);
|
||||
parser.setKind(ASTParser.K_COMPILATION_UNIT);
|
||||
parser.setStatementsRecovery(true);
|
||||
parser.setBindingsRecovery(true);
|
||||
parser.setResolveBindings(true);
|
||||
|
||||
String[] classpathEntries = getClasspathEntries(document);
|
||||
String[] sourceEntries = new String[] {};
|
||||
parser.setEnvironment(classpathEntries, sourceEntries, null, true);
|
||||
|
||||
String docURI = document.getUri();
|
||||
String unitName = docURI.substring(docURI.lastIndexOf("/"));
|
||||
parser.setUnitName(unitName);
|
||||
parser.setSource(document.get(0, document.getLength()).toCharArray());
|
||||
|
||||
CompilationUnit cu = (CompilationUnit) parser.createAST(null);
|
||||
ASTNode node = NodeFinder.perform(cu, offset, 0);
|
||||
|
||||
if (node != null) {
|
||||
return provideReferencesForAnnotation(node, offset, document);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private CompletableFuture<List<? extends Location>> provideReferencesForAnnotation(ASTNode node, int offset, TextDocument doc) {
|
||||
Annotation annotation = null;
|
||||
|
||||
while (node != null && !(node instanceof Annotation)) {
|
||||
node = node.getParent();
|
||||
}
|
||||
|
||||
if (node != null) {
|
||||
annotation = (Annotation) node;
|
||||
ITypeBinding type = annotation.resolveTypeBinding();
|
||||
if (type != null) {
|
||||
String qualifiedName = type.getQualifiedName();
|
||||
if (qualifiedName != null) {
|
||||
ReferenceProvider provider = this.referenceProviders.get(qualifiedName);
|
||||
if (provider != null) {
|
||||
return provider.provideReferences(node, annotation, type, offset, doc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private String[] getClasspathEntries(IDocument doc) throws Exception {
|
||||
IJavaProject project = this.projectFinder.find(new TextDocumentIdentifier(doc.getUri())).get();
|
||||
IClasspath classpath = project.getClasspath();
|
||||
Stream<Path> classpathEntries = classpath.getClasspathEntries().stream();
|
||||
return classpathEntries
|
||||
.filter(path -> path.toFile().exists())
|
||||
.map(path -> path.toAbsolutePath().toString()).toArray(String[]::new);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.handlers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.lsp4j.SymbolInformation;
|
||||
import org.eclipse.lsp4j.WorkspaceSymbolParams;
|
||||
import org.springframework.ide.vscode.boot.java.requestmapping.LiveAppURLSymbolProvider;
|
||||
import org.springframework.ide.vscode.boot.java.utils.SpringIndexer;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.WorkspaceSymbolHandler;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class BootJavaWorkspaceSymbolHandler implements WorkspaceSymbolHandler {
|
||||
|
||||
private final SpringIndexer indexer;
|
||||
private final LiveAppURLSymbolProvider liveAppSymbolProvider;
|
||||
|
||||
public BootJavaWorkspaceSymbolHandler(SpringIndexer indexer, LiveAppURLSymbolProvider liveAppSymbolProvider) {
|
||||
this.indexer = indexer;
|
||||
this.liveAppSymbolProvider = liveAppSymbolProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends SymbolInformation> handle(WorkspaceSymbolParams params) {
|
||||
if (params.getQuery() != null && params.getQuery().startsWith("//")) {
|
||||
return liveAppSymbolProvider.getSymbols(params.getQuery());
|
||||
}
|
||||
else {
|
||||
return indexer.getAllSymbols(params.getQuery());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.handlers;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public interface CompletionProvider {
|
||||
|
||||
Collection<ICompletionProposal> provideCompletions(ASTNode node, Annotation annotation, ITypeBinding type, int offset, IDocument doc);
|
||||
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2018 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.handlers;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.TypeDeclaration;
|
||||
import org.eclipse.lsp4j.Hover;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public interface HoverProvider {
|
||||
|
||||
Hover provideHover(ASTNode node, Annotation annotation, ITypeBinding type, int offset, TextDocument doc, IJavaProject project, SpringBootApp[] runningApps);
|
||||
Hover provideHover(ASTNode node, TypeDeclaration typeDeclaration, ITypeBinding type, int offset, TextDocument doc, IJavaProject project, SpringBootApp[] runningApps);
|
||||
|
||||
Collection<Range> getLiveHoverHints(Annotation annotation, TextDocument doc, SpringBootApp[] runningApps);
|
||||
Collection<Range> getLiveHoverHints(TypeDeclaration typeDeclaration, TextDocument doc, SpringBootApp[] runningApps);
|
||||
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.handlers;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.lsp4j.Location;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public interface ReferenceProvider {
|
||||
|
||||
CompletableFuture<List<? extends Location>> provideReferences(ASTNode node, Annotation annotation,
|
||||
ITypeBinding type, int offset, TextDocument doc);
|
||||
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.handlers;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
public interface RunningAppProvider {
|
||||
|
||||
public static final RunningAppProvider DEFAULT = SpringBootApp::getAllRunningSpringApps;
|
||||
public static final RunningAppProvider NULL = () -> ImmutableList.of();
|
||||
|
||||
Collection<SpringBootApp> getAllRunningSpringApps() throws Exception;
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2018 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.handlers;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.TypeDeclaration;
|
||||
import org.eclipse.lsp4j.SymbolInformation;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
public interface SymbolProvider {
|
||||
|
||||
Collection<SymbolInformation> getSymbols(Annotation node, ITypeBinding typeBinding, Collection<ITypeBinding> metaAnnotations, TextDocument doc);
|
||||
Collection<SymbolInformation> getSymbols(TypeDeclaration typeDeclaration, TextDocument doc);
|
||||
|
||||
}
|
||||
@@ -1,558 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Derived from:
|
||||
* org.eclipse.jdt.core.dom.rewrite.ImportRewrite
|
||||
*
|
||||
* for use in STS4, where IProject and ICompilationUnit are not available when parsing a Java source.
|
||||
*
|
||||
* Original license:
|
||||
*
|
||||
* Copyright (c) 2000, 2016 IBM Corporation and others.
|
||||
* 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* IBM Corporation - initial API and implementation
|
||||
* John Glassmyer <jogl@google.com> - import group sorting is broken - https://bugs.eclipse.org/430303
|
||||
* Lars Vogel <Lars.Vogel@vogella.com> - Contributions for
|
||||
* Bug 473178
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.jdt.imports;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.jdt.core.ICompilationUnit;
|
||||
import org.eclipse.jdt.core.compiler.CharOperation;
|
||||
import org.eclipse.jdt.core.dom.ASTParser;
|
||||
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
|
||||
import org.eclipse.jdt.core.dom.Comment;
|
||||
import org.eclipse.jdt.core.dom.CompilationUnit;
|
||||
import org.eclipse.jdt.core.dom.ImportDeclaration;
|
||||
import org.eclipse.jdt.core.dom.PackageDeclaration;
|
||||
import org.eclipse.jdt.core.dom.PrimitiveType;
|
||||
import org.eclipse.jdt.core.dom.SimpleName;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
|
||||
|
||||
/**
|
||||
* The {@link ImportRewrite} helps updating imports following a import order and on-demand imports threshold as configured by a project.
|
||||
* <p>
|
||||
* The import rewrite is created on a compilation unit and collects references to types that are added or removed. When adding imports, e.g. using
|
||||
* {@link #addImport(String)}, the import rewrite evaluates if the type can be imported and returns the a reference to the type that can be used in code.
|
||||
* This reference is either unqualified if the import could be added, or fully qualified if the import failed due to a conflict with another element of the same name.
|
||||
* </p>
|
||||
* <p>
|
||||
* On {@link #rewriteImports(IProgressMonitor)} the rewrite translates these descriptions into
|
||||
* text edits that can then be applied to the original source. The rewrite infrastructure tries to generate minimal text changes and only
|
||||
* works on the import statements. It is possible to combine the result of an import rewrite with the result of a {@link org.eclipse.jdt.core.dom.rewrite.ASTRewrite}
|
||||
* as long as no import statements are modified by the AST rewrite.
|
||||
* </p>
|
||||
* <p>The options controlling the import order and on-demand thresholds are:
|
||||
* <ul><li>{@link #setImportOrder(String[])} specifies the import groups and their preferred order</li>
|
||||
* <li>{@link #setOnDemandImportThreshold(int)} specifies the number of imports in a group needed for a on-demand import statement (star import)</li>
|
||||
* <li>{@link #setStaticOnDemandImportThreshold(int)} specifies the number of static imports in a group needed for a on-demand import statement (star import)</li>
|
||||
*</ul>
|
||||
* This class is not intended to be subclassed.
|
||||
* </p>
|
||||
* @since 3.2
|
||||
*/
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public final class ImportRewrite {
|
||||
|
||||
/**
|
||||
* A {@link ImportRewrite.ImportRewriteContext} can optionally be used in e.g. {@link ImportRewrite#addImport(String, ImportRewrite.ImportRewriteContext)} to
|
||||
* give more information about the types visible in the scope. These types can be for example inherited inner types where it is
|
||||
* unnecessary to add import statements for.
|
||||
*
|
||||
* </p>
|
||||
* <p>
|
||||
* This class can be implemented by clients.
|
||||
* </p>
|
||||
*/
|
||||
public static abstract class ImportRewriteContext {
|
||||
|
||||
/**
|
||||
* Result constant signaling that the given element is know in the context.
|
||||
*/
|
||||
public final static int RES_NAME_FOUND= 1;
|
||||
|
||||
/**
|
||||
* Result constant signaling that the given element is not know in the context.
|
||||
*/
|
||||
public final static int RES_NAME_UNKNOWN= 2;
|
||||
|
||||
/**
|
||||
* Result constant signaling that the given element is conflicting with an other element in the context.
|
||||
*/
|
||||
public final static int RES_NAME_CONFLICT= 3;
|
||||
|
||||
/**
|
||||
* Result constant signaling that the given element must be imported explicitly (and must not be folded into
|
||||
* an on-demand import or filtered as an implicit import).
|
||||
*
|
||||
* @since 3.11
|
||||
*/
|
||||
public final static int RES_NAME_UNKNOWN_NEEDS_EXPLICIT_IMPORT= 4;
|
||||
|
||||
/**
|
||||
* Kind constant specifying that the element is a type import.
|
||||
*/
|
||||
public final static int KIND_TYPE= 1;
|
||||
|
||||
/**
|
||||
* Kind constant specifying that the element is a static field import.
|
||||
*/
|
||||
public final static int KIND_STATIC_FIELD= 2;
|
||||
|
||||
/**
|
||||
* Kind constant specifying that the element is a static method import.
|
||||
*/
|
||||
public final static int KIND_STATIC_METHOD= 3;
|
||||
|
||||
/**
|
||||
* Searches for the given element in the context and reports if the element is known ({@link #RES_NAME_FOUND}),
|
||||
* unknown ({@link #RES_NAME_UNKNOWN}), unknown in the context but known to require an explicit import
|
||||
* ({@link #RES_NAME_UNKNOWN_NEEDS_EXPLICIT_IMPORT}), or if its name conflicts ({@link #RES_NAME_CONFLICT})
|
||||
* with an other element.
|
||||
*
|
||||
* @param qualifier The qualifier of the element, can be package or the qualified name of a type
|
||||
* @param name The simple name of the element; either a type, method or field name or * for on-demand imports.
|
||||
* @param kind The kind of the element. Can be either {@link #KIND_TYPE}, {@link #KIND_STATIC_FIELD} or
|
||||
* {@link #KIND_STATIC_METHOD}. Implementors should be prepared for new, currently unspecified kinds and return
|
||||
* {@link #RES_NAME_UNKNOWN} by default.
|
||||
* @return Returns the result of the lookup. Can be either {@link #RES_NAME_FOUND}, {@link #RES_NAME_UNKNOWN},
|
||||
* {@link #RES_NAME_CONFLICT}, or {@link #RES_NAME_UNKNOWN_NEEDS_EXPLICIT_IMPORT}.
|
||||
*/
|
||||
public abstract int findInContext(String qualifier, String name, int kind);
|
||||
}
|
||||
|
||||
private static final char STATIC_PREFIX= 's';
|
||||
private static final char NORMAL_PREFIX= 'n';
|
||||
|
||||
private final ImportRewriteContext defaultContext;
|
||||
|
||||
private final CompilationUnit astRoot;
|
||||
|
||||
private final boolean restoreExistingImports;
|
||||
private final List existingImports;
|
||||
|
||||
|
||||
private List<String> addedImports;
|
||||
|
||||
/**
|
||||
* Simple names of non-static imports which must not be reduced into on-demand imports
|
||||
* or filtered out as implicit.
|
||||
*/
|
||||
private Set<String> typeExplicitSimpleNames;
|
||||
|
||||
|
||||
private boolean filterImplicitImports;
|
||||
private boolean useContextToFilterImplicitImports;
|
||||
|
||||
|
||||
/**
|
||||
* Creates an {@link ImportRewrite} from an AST ({@link CompilationUnit}). The AST has to be created from an
|
||||
* {@link ICompilationUnit}, that means {@link ASTParser#setSource(ICompilationUnit)} has been used when creating the
|
||||
* AST. If <code>restoreExistingImports</code> is <code>true</code>, all existing imports are kept, and new imports
|
||||
* will be inserted at best matching locations. If <code>restoreExistingImports</code> is <code>false</code>, the
|
||||
* existing imports will be removed and only the newly added imports will be created.
|
||||
* <p>
|
||||
* Note that this method is more efficient than using {@link #create(ICompilationUnit, boolean)} if an AST is already available.
|
||||
* </p>
|
||||
* @param astRoot the AST root node to create the imports for
|
||||
* @param restoreExistingImports specifies if the existing imports should be kept or removed.
|
||||
* @return the created import rewriter.
|
||||
* @throws IllegalArgumentException thrown when the passed AST is null or was not created from a compilation unit.
|
||||
*/
|
||||
public static ImportRewrite create(CompilationUnit astRoot, boolean restoreExistingImports) {
|
||||
if (astRoot == null) {
|
||||
throw new IllegalArgumentException("AST must not be null"); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
List existingImport= null;
|
||||
if (restoreExistingImports) {
|
||||
existingImport= new ArrayList();
|
||||
List imports= astRoot.imports();
|
||||
for (int i= 0; i < imports.size(); i++) {
|
||||
ImportDeclaration curr= (ImportDeclaration) imports.get(i);
|
||||
StringBuffer buf= new StringBuffer();
|
||||
buf.append(curr.isStatic() ? STATIC_PREFIX : NORMAL_PREFIX).append(curr.getName().getFullyQualifiedName());
|
||||
if (curr.isOnDemand()) {
|
||||
if (buf.length() > 1)
|
||||
buf.append('.');
|
||||
buf.append('*');
|
||||
}
|
||||
existingImport.add(buf.toString());
|
||||
}
|
||||
}
|
||||
return new ImportRewrite(astRoot, existingImport);
|
||||
}
|
||||
|
||||
private ImportRewrite(CompilationUnit astRoot, List existingImports) {
|
||||
this.astRoot= astRoot; // might be null
|
||||
if (existingImports != null) {
|
||||
this.existingImports= existingImports;
|
||||
this.restoreExistingImports= !existingImports.isEmpty();
|
||||
} else {
|
||||
this.existingImports= new ArrayList();
|
||||
this.restoreExistingImports= false;
|
||||
}
|
||||
this.filterImplicitImports= true;
|
||||
// consider that no contexts are used
|
||||
this.useContextToFilterImplicitImports = false;
|
||||
|
||||
this.defaultContext= new ImportRewriteContext() {
|
||||
@Override
|
||||
public int findInContext(String qualifier, String name, int kind) {
|
||||
return findInImports(qualifier, name, kind);
|
||||
}
|
||||
};
|
||||
this.addedImports= new ArrayList<>();
|
||||
this.typeExplicitSimpleNames = new HashSet<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default rewrite context that only knows about the imported types. Clients
|
||||
* can write their own context and use the default context for the default behavior.
|
||||
* @return the default import rewrite context.
|
||||
*/
|
||||
public ImportRewriteContext getDefaultImportRewriteContext() {
|
||||
return this.defaultContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies that implicit imports (for types in <code>java.lang</code>, types in the same package as the rewrite
|
||||
* compilation unit, and types in the compilation unit's main type) should not be created, except if necessary to
|
||||
* resolve an on-demand import conflict.
|
||||
* <p>
|
||||
* The filter is enabled by default.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note: {@link #setUseContextToFilterImplicitImports(boolean)} can be used to filter implicit imports
|
||||
* when a context is used.
|
||||
* </p>
|
||||
*
|
||||
* @param filterImplicitImports
|
||||
* if <code>true</code>, implicit imports will be filtered
|
||||
*
|
||||
* @see #setUseContextToFilterImplicitImports(boolean)
|
||||
*/
|
||||
public void setFilterImplicitImports(boolean filterImplicitImports) {
|
||||
this.filterImplicitImports= filterImplicitImports;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether a context should be used to properly filter implicit imports.
|
||||
* <p>
|
||||
* By default, the option is disabled to preserve pre-3.6 behavior.
|
||||
* </p>
|
||||
* <p>
|
||||
* When this option is set, the context passed to the <code>addImport*(...)</code> methods is used to determine
|
||||
* whether an import can be filtered because the type is implicitly visible. Note that too many imports
|
||||
* may be kept if this option is set and <code>addImport*(...)</code> methods are called without a context.
|
||||
* </p>
|
||||
*
|
||||
* @param useContextToFilterImplicitImports the given setting
|
||||
*
|
||||
* @see #setFilterImplicitImports(boolean)
|
||||
* @since 3.6
|
||||
*/
|
||||
public void setUseContextToFilterImplicitImports(boolean useContextToFilterImplicitImports) {
|
||||
this.useContextToFilterImplicitImports = useContextToFilterImplicitImports;
|
||||
}
|
||||
|
||||
private static int compareImport(char prefix, String qualifier, String name, String curr) {
|
||||
if (curr.charAt(0) != prefix || !curr.endsWith(name)) {
|
||||
return ImportRewriteContext.RES_NAME_UNKNOWN;
|
||||
}
|
||||
|
||||
curr= curr.substring(1); // remove the prefix
|
||||
|
||||
if (curr.length() == name.length()) {
|
||||
if (qualifier.length() == 0) {
|
||||
return ImportRewriteContext.RES_NAME_FOUND;
|
||||
}
|
||||
return ImportRewriteContext.RES_NAME_CONFLICT;
|
||||
}
|
||||
// at this place: curr.length > name.length
|
||||
|
||||
int dotPos= curr.length() - name.length() - 1;
|
||||
if (curr.charAt(dotPos) != '.') {
|
||||
return ImportRewriteContext.RES_NAME_UNKNOWN;
|
||||
}
|
||||
if (qualifier.length() != dotPos || !curr.startsWith(qualifier)) {
|
||||
return ImportRewriteContext.RES_NAME_CONFLICT;
|
||||
}
|
||||
return ImportRewriteContext.RES_NAME_FOUND;
|
||||
}
|
||||
|
||||
/**
|
||||
* Not API, package visibility as accessed from an anonymous type
|
||||
*/
|
||||
/* package */ final int findInImports(String qualifier, String name, int kind) {
|
||||
boolean allowAmbiguity= (kind == ImportRewriteContext.KIND_STATIC_METHOD) || (name.length() == 1 && name.charAt(0) == '*');
|
||||
List imports= this.existingImports;
|
||||
char prefix= (kind == ImportRewriteContext.KIND_TYPE) ? NORMAL_PREFIX : STATIC_PREFIX;
|
||||
|
||||
for (int i= imports.size() - 1; i >= 0 ; i--) {
|
||||
String curr= (String) imports.get(i);
|
||||
int res= compareImport(prefix, qualifier, name, curr);
|
||||
if (res != ImportRewriteContext.RES_NAME_UNKNOWN) {
|
||||
if (!allowAmbiguity || res == ImportRewriteContext.RES_NAME_FOUND) {
|
||||
if (prefix != STATIC_PREFIX) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String packageName = getPackageName();
|
||||
if (kind == ImportRewriteContext.KIND_TYPE) {
|
||||
if (this.filterImplicitImports && this.useContextToFilterImplicitImports) {
|
||||
|
||||
// [STS4] No ICompilationUnit available as there is no class file or associated IJavaElement available for the source
|
||||
|
||||
// String mainTypeSimpleName= JavaCore.removeJavaLikeExtension(this.compilationUnit.getElementName());
|
||||
// String mainTypeName= Util.concatenateName(packageName, mainTypeSimpleName, '.');
|
||||
// if (qualifier.equals(packageName)
|
||||
// || mainTypeName.equals(Util.concatenateName(qualifier, name, '.'))) {
|
||||
// return ImportRewriteContext.RES_NAME_FOUND;
|
||||
// }
|
||||
|
||||
if (this.astRoot != null) {
|
||||
List<AbstractTypeDeclaration> types = this.astRoot.types();
|
||||
int nTypes = types.size();
|
||||
for (int i = 0; i < nTypes; i++) {
|
||||
AbstractTypeDeclaration type = types.get(i);
|
||||
SimpleName simpleName = type.getName();
|
||||
if (simpleName.getIdentifier().equals(name)) {
|
||||
return qualifier.equals(packageName)
|
||||
? ImportRewriteContext.RES_NAME_FOUND
|
||||
: ImportRewriteContext.RES_NAME_CONFLICT;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
// [STS4] No ICompilationUnit available as there is no class file or associated IJavaElement available for the source
|
||||
// try {
|
||||
// IType[] types = this.compilationUnit.getTypes();
|
||||
// int nTypes = types.length;
|
||||
// for (int i = 0; i < nTypes; i++) {
|
||||
// IType type = types[i];
|
||||
// String typeName = type.getElementName();
|
||||
// if (typeName.equals(name)) {
|
||||
// return qualifier.equals(packageName)
|
||||
// ? ImportRewriteContext.RES_NAME_FOUND
|
||||
// : ImportRewriteContext.RES_NAME_CONFLICT;
|
||||
// }
|
||||
// }
|
||||
// } catch (JavaModelException e) {
|
||||
// // don't want to throw an exception here
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ImportRewriteContext.RES_NAME_UNKNOWN;
|
||||
}
|
||||
|
||||
private String getPackageName() {
|
||||
// [STS4] No ICompilationUnit available as there is no class file or associated IJavaElement available for the source
|
||||
// this.compilationUnit.getParent().getElementName();
|
||||
return this.astRoot.getPackage().getName().getFullyQualifiedName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new import to the rewriter's record and returns a type reference that can be used
|
||||
* in the code. The type binding can only be an array or non-generic type.
|
||||
* <p>
|
||||
* No imports are added for types that are already known. If a import for a type is recorded to be removed, this record is discarded instead.
|
||||
* </p>
|
||||
* <p>
|
||||
* The content of the compilation unit itself is actually not modified
|
||||
* in any way by this method; rather, the rewriter just records that a new import has been added.
|
||||
* </p>
|
||||
* @param qualifiedTypeName the qualified type name of the type to be added
|
||||
* @param context an optional context that knows about types visible in the current scope or <code>null</code>
|
||||
* to use the default context only using the available imports.
|
||||
* @return a type reference for the given qualified type name. The type name is a simple name if an import could be used,
|
||||
* or else a qualified name if an import conflict prevented an import.
|
||||
*/
|
||||
public String addImport(String qualifiedTypeName, ImportRewriteContext context) {
|
||||
int angleBracketOffset= qualifiedTypeName.indexOf('<');
|
||||
if (angleBracketOffset != -1) {
|
||||
return internalAddImport(qualifiedTypeName.substring(0, angleBracketOffset), context) + qualifiedTypeName.substring(angleBracketOffset);
|
||||
}
|
||||
int bracketOffset= qualifiedTypeName.indexOf('[');
|
||||
if (bracketOffset != -1) {
|
||||
return internalAddImport(qualifiedTypeName.substring(0, bracketOffset), context) + qualifiedTypeName.substring(bracketOffset);
|
||||
}
|
||||
return internalAddImport(qualifiedTypeName, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new import to the rewriter's record and returns a type reference that can be used
|
||||
* in the code. The type binding can only be an array or non-generic type.
|
||||
* <p>
|
||||
* No imports are added for types that are already known. If a import for a type is recorded to be removed, this record is discarded instead.
|
||||
* </p>
|
||||
* <p>
|
||||
* The content of the compilation unit itself is actually not modified
|
||||
* in any way by this method; rather, the rewriter just records that a new import has been added.
|
||||
* </p>
|
||||
* @param qualifiedTypeName the qualified type name of the type to be added
|
||||
* @return a type reference for the given qualified type name. The type name is a simple name if an import could be used,
|
||||
* or else a qualified name if an import conflict prevented an import.
|
||||
*/
|
||||
public String addImport(String qualifiedTypeName) {
|
||||
return addImport(qualifiedTypeName, this.defaultContext);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private String internalAddImport(String fullTypeName, ImportRewriteContext context) {
|
||||
int idx= fullTypeName.lastIndexOf('.');
|
||||
String typeContainerName, typeName;
|
||||
if (idx != -1) {
|
||||
typeContainerName= fullTypeName.substring(0, idx);
|
||||
typeName= fullTypeName.substring(idx + 1);
|
||||
} else {
|
||||
typeContainerName= ""; //$NON-NLS-1$
|
||||
typeName= fullTypeName;
|
||||
}
|
||||
|
||||
if (typeContainerName.length() == 0 && PrimitiveType.toCode(typeName) != null) {
|
||||
return fullTypeName;
|
||||
}
|
||||
|
||||
if (context == null)
|
||||
context= this.defaultContext;
|
||||
|
||||
int res= context.findInContext(typeContainerName, typeName, ImportRewriteContext.KIND_TYPE);
|
||||
if (res == ImportRewriteContext.RES_NAME_CONFLICT) {
|
||||
return fullTypeName;
|
||||
}
|
||||
if (res == ImportRewriteContext.RES_NAME_UNKNOWN) {
|
||||
addEntry(NORMAL_PREFIX + fullTypeName);
|
||||
}
|
||||
if (res == ImportRewriteContext.RES_NAME_UNKNOWN_NEEDS_EXPLICIT_IMPORT) {
|
||||
addEntry(NORMAL_PREFIX + fullTypeName);
|
||||
this.typeExplicitSimpleNames.add(typeName);
|
||||
}
|
||||
return typeName;
|
||||
}
|
||||
|
||||
private void addEntry(String entry) {
|
||||
this.existingImports.add(entry);
|
||||
|
||||
this.addedImports.add(entry);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns all non-static imports that are recorded to be added.
|
||||
*
|
||||
* @return the imports recorded to be added.
|
||||
*/
|
||||
public String[] getAddedImports() {
|
||||
return filterFromList(this.addedImports, NORMAL_PREFIX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if imports have been recorded to be added or removed.
|
||||
* @return boolean returns if any changes to imports have been recorded.
|
||||
*/
|
||||
public boolean hasRecordedChanges() {
|
||||
return !this.restoreExistingImports
|
||||
|| !this.addedImports.isEmpty();
|
||||
}
|
||||
|
||||
private static String[] filterFromList(List<String> imports, char prefix) {
|
||||
if (imports == null) {
|
||||
return CharOperation.NO_STRINGS;
|
||||
}
|
||||
List<String> res= new ArrayList<>();
|
||||
for (String curr : imports) {
|
||||
if (prefix == curr.charAt(0)) {
|
||||
res.add(curr.substring(1));
|
||||
}
|
||||
}
|
||||
return res.toArray(new String[res.size()]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads the positions of each existing import declaration along with any associated comments,
|
||||
* and returns these in a list whose iteration order reflects the existing order of the imports
|
||||
* in the compilation unit.
|
||||
*/
|
||||
private int getAddedImportsInsertLocation() {
|
||||
List<ImportDeclaration> importDeclarations = astRoot.imports();
|
||||
|
||||
if (importDeclarations == null) {
|
||||
importDeclarations = Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Comment> comments = astRoot.getCommentList();
|
||||
|
||||
int currentCommentIndex = 0;
|
||||
|
||||
// Skip over package and file header comments (see https://bugs.eclipse.org/121428).
|
||||
ImportDeclaration firstImport = importDeclarations.get(0);
|
||||
PackageDeclaration packageDeclaration = astRoot.getPackage();
|
||||
int firstImportStartPosition = packageDeclaration == null
|
||||
? firstImport.getStartPosition()
|
||||
: astRoot.getExtendedStartPosition(packageDeclaration)
|
||||
+ astRoot.getExtendedLength(packageDeclaration);
|
||||
while (currentCommentIndex < comments.size()
|
||||
&& comments.get(currentCommentIndex).getStartPosition() < firstImportStartPosition) {
|
||||
currentCommentIndex++;
|
||||
}
|
||||
|
||||
int previousExtendedEndPosition = -1;
|
||||
for (ImportDeclaration currentImport : importDeclarations) {
|
||||
int extendedEndPosition = astRoot.getExtendedStartPosition(currentImport)
|
||||
+ astRoot.getExtendedLength(currentImport);
|
||||
|
||||
int commentAfterImportIndex = currentCommentIndex;
|
||||
while (commentAfterImportIndex < comments.size()
|
||||
&& comments.get(commentAfterImportIndex).getStartPosition() < extendedEndPosition) {
|
||||
commentAfterImportIndex++;
|
||||
}
|
||||
|
||||
|
||||
currentCommentIndex = commentAfterImportIndex;
|
||||
previousExtendedEndPosition = extendedEndPosition;
|
||||
}
|
||||
|
||||
return previousExtendedEndPosition;
|
||||
}
|
||||
|
||||
public DocumentEdits createEdit(IDocument doc) {
|
||||
DocumentEdits edits = null;
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
|
||||
String[] createdImprts = getAddedImports();
|
||||
if (createdImprts != null && createdImprts.length >0) {
|
||||
edits =new DocumentEdits(doc);
|
||||
buffer.append('\n');
|
||||
for (String imp : createdImprts) {
|
||||
buffer.append("import ");
|
||||
buffer.append(imp);
|
||||
buffer.append(';');
|
||||
buffer.append('\n');
|
||||
}
|
||||
edits.insert(getAddedImportsInsertLocation(), buffer.toString());
|
||||
|
||||
}
|
||||
return edits;
|
||||
}
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.livehover;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.lsp4j.Hover;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.eclipse.lsp4j.jsonrpc.messages.Either;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.HoverProvider;
|
||||
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBeansModel;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider {
|
||||
|
||||
@Override
|
||||
public Collection<Range> getLiveHoverHints(Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) {
|
||||
// Highlight if any running app contains an instance of this component
|
||||
try {
|
||||
if (runningApps.length > 0) {
|
||||
LiveBean definedBean = getDefinedBean(annotation);
|
||||
if (definedBean != null) {
|
||||
if (Stream.of(runningApps).anyMatch(app -> LiveHoverUtils.hasRelevantBeans(app, definedBean))) {
|
||||
Optional<Range> nameRange = ASTUtils.nameRange(doc, annotation);
|
||||
if (nameRange.isPresent()) {
|
||||
return ImmutableList.of(nameRange.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
}
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Hover provideHover(ASTNode node, Annotation annotation, ITypeBinding type, int offset,
|
||||
TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) {
|
||||
if (runningApps.length > 0) {
|
||||
|
||||
LiveBean definedBean = getDefinedBean(annotation);
|
||||
if (definedBean != null) {
|
||||
StringBuilder hover = new StringBuilder();
|
||||
hover.append("**Injection report for " + LiveHoverUtils.showBean(definedBean) + "**\n\n");
|
||||
|
||||
boolean hasInterestingApp = false;
|
||||
for (SpringBootApp app : runningApps) {
|
||||
LiveBeansModel beans = app.getBeans();
|
||||
List<LiveBean> relevantBeans = LiveHoverUtils.findRelevantBeans(app, definedBean).collect(Collectors.toList());
|
||||
|
||||
if (!relevantBeans.isEmpty()) {
|
||||
if (!hasInterestingApp) {
|
||||
hasInterestingApp = true;
|
||||
} else {
|
||||
hover.append("\n\n");
|
||||
}
|
||||
hover.append(LiveHoverUtils.niceAppName(app) + ":");
|
||||
|
||||
for (LiveBean bean : relevantBeans) {
|
||||
addInjectedInto(definedBean, hover, beans, bean, project);
|
||||
addAutomaticallyWiredContructor(hover, annotation, beans, bean, project);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasInterestingApp) {
|
||||
return new Hover(ImmutableList.of(Either.forLeft(hover.toString())));
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected abstract LiveBean getDefinedBean(Annotation annotation);
|
||||
|
||||
protected void addAutomaticallyWiredContructor(StringBuilder hover, Annotation annotation, LiveBeansModel beans, LiveBean bean, IJavaProject project) {
|
||||
//This doesn't really belong here, but it accomodates Martin's additional logic to handle implicitly
|
||||
//@Autowired constructor.
|
||||
//This does nothing by default as its really only relevant to @Component annotation report.
|
||||
}
|
||||
|
||||
protected void addInjectedInto(LiveBean definedBean, StringBuilder hover, LiveBeansModel beans, LiveBean bean, IJavaProject project) {
|
||||
hover.append("\n\n");
|
||||
List<LiveBean> dependers = beans.getBeansDependingOn(bean.getId());
|
||||
if (dependers.isEmpty()) {
|
||||
hover.append(LiveHoverUtils.showBean(bean) + " exists but is **Not injected anywhere**\n");
|
||||
} else {
|
||||
hover.append(LiveHoverUtils.showBean(bean) + " injected into:\n\n");
|
||||
boolean firstDependency = true;
|
||||
for (LiveBean dependingBean : dependers) {
|
||||
if (!firstDependency) {
|
||||
hover.append("\n");
|
||||
}
|
||||
hover.append("- " + LiveHoverUtils.showBeanWithResource(dependingBean, " ", project));
|
||||
firstDependency = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2018 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.livehover;
|
||||
|
||||
import static org.springframework.ide.vscode.boot.java.utils.ASTUtils.nameRange;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.ASTVisitor;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.StringLiteral;
|
||||
import org.eclipse.jdt.core.dom.TypeDeclaration;
|
||||
import org.eclipse.lsp4j.Hover;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.eclipse.lsp4j.jsonrpc.messages.Either;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.HoverProvider;
|
||||
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableList.Builder;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
/**
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
public class ActiveProfilesProvider implements HoverProvider {
|
||||
|
||||
@Override
|
||||
public Hover provideHover(
|
||||
ASTNode node,
|
||||
Annotation annotation,
|
||||
ITypeBinding type,
|
||||
int offset,
|
||||
TextDocument doc, IJavaProject project, SpringBootApp[] runningApps
|
||||
) {
|
||||
if (runningApps.length>0) {
|
||||
StringBuilder markdown = new StringBuilder();
|
||||
markdown.append("**Active Profiles**\n\n");
|
||||
boolean hasInterestingApp = false;
|
||||
for (SpringBootApp app : runningApps) {
|
||||
List<String> profiles = app.getActiveProfiles();
|
||||
if (profiles==null) {
|
||||
markdown.append(niceAppName(app)+" : _Unknown_\n\n");
|
||||
} else {
|
||||
hasInterestingApp = true;
|
||||
if (profiles.isEmpty()) {
|
||||
markdown.append(niceAppName(app)+" : _None_\n\n");
|
||||
} else {
|
||||
markdown.append(niceAppName(app)+" :\n");
|
||||
for (String profile : profiles) {
|
||||
markdown.append("- "+profile+"\n");
|
||||
}
|
||||
markdown.append("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasInterestingApp) {
|
||||
return new Hover(
|
||||
ImmutableList.of(Either.forLeft(markdown.toString()))
|
||||
);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String niceAppName(SpringBootApp app) {
|
||||
return "Process [PID="+app.getProcessID()+", name=`"+app.getProcessName()+"`]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Range> getLiveHoverHints(Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) {
|
||||
if (runningApps.length > 0) {
|
||||
Builder<Range> ranges = ImmutableList.builder();
|
||||
nameRange(doc, annotation).ifPresent(ranges::add);
|
||||
|
||||
Set<String> allActiveProfiles = getAllActiveProfiles(runningApps);
|
||||
annotation.accept(new ASTVisitor() {
|
||||
@Override
|
||||
public boolean visit(StringLiteral node) {
|
||||
String value = ASTUtils.getLiteralValue(node);
|
||||
if (value!=null && allActiveProfiles.contains(value)) {
|
||||
rangeOf(doc, node).ifPresent(ranges::add);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return ranges.build();
|
||||
}
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
private static Set<String> getAllActiveProfiles(SpringBootApp[] runningApps) {
|
||||
ImmutableSet.Builder<String> builder = ImmutableSet.builder();
|
||||
for (SpringBootApp app : runningApps) {
|
||||
List<String> profiles = app.getActiveProfiles();
|
||||
if (profiles!=null) {
|
||||
builder.addAll(app.getActiveProfiles());
|
||||
}
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private static Optional<Range> rangeOf(TextDocument doc, StringLiteral node) {
|
||||
try {
|
||||
int start = node.getStartPosition();
|
||||
int end = start + node.getLength();
|
||||
if (doc.getSafeChar(start)=='"') {
|
||||
start++;
|
||||
}
|
||||
if (doc.getSafeChar(end-1)=='"') {
|
||||
end--;
|
||||
}
|
||||
return Optional.of(doc.toRange(start, end-start));
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Hover provideHover(ASTNode node, TypeDeclaration typeDeclaration, ITypeBinding type, int offset,
|
||||
TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Range> getLiveHoverHints(TypeDeclaration typeDeclaration, TextDocument doc,
|
||||
SpringBootApp[] runningApps) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2018 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.livehover;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.MethodDeclaration;
|
||||
import org.eclipse.jdt.core.dom.TypeDeclaration;
|
||||
import org.eclipse.lsp4j.Hover;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.util.Optionals;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
public class BeanInjectedIntoHoverProvider extends AbstractInjectedIntoHoverProvider {
|
||||
|
||||
@Override
|
||||
protected LiveBean getDefinedBean(Annotation annotation) {
|
||||
MethodDeclaration beanMethod = ASTUtils.getAnnotatedMethod(annotation);
|
||||
if (beanMethod!=null) {
|
||||
Optional<String> beanId = getBeanId(annotation, beanMethod);
|
||||
if (beanId.isPresent()) {
|
||||
//TODO: we could try to be more precise here and determine the bean type from the
|
||||
//ITypeBinding beanType = null; //null means unknown
|
||||
// method signature, however, this will typically give us a more abstract type than
|
||||
// the actual bean type at runtime. So if we we do that we have to deal with that
|
||||
// somehow. Therefore, for the time being we leave the beanType as `unknown` and
|
||||
// so do not use the bean type in determining relevant beans.
|
||||
// Type unresolvedBeanType = beanMethod.getReturnType2();
|
||||
// if (unresolvedBeanType!=null) {
|
||||
// beanType = unresolvedBeanType.resolveBinding();
|
||||
// }
|
||||
return LiveBean.builder()
|
||||
.id(beanId.get())
|
||||
// .type(type)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Optional<String> getBeanId(Annotation annotation, MethodDeclaration beanMethod) {
|
||||
//Note: must handle all these cases:
|
||||
// @Bean
|
||||
// @Bean("beanId")
|
||||
// @Bean({"beanId", "alias1"})
|
||||
// @Bean(value="beanId")
|
||||
// @Bean(value={"beanId", "alias1"})
|
||||
// @Bean(name="beanId", ...)
|
||||
// @Bean(name={"beanId", "alias1"}, ...)
|
||||
return Optionals.tryInOrder(
|
||||
() -> ASTUtils.getAttribute(annotation, "value").flatMap(ASTUtils::getFirstString),
|
||||
() -> ASTUtils.getAttribute(annotation, "name").flatMap(ASTUtils::getFirstString),
|
||||
() -> Optional.ofNullable(beanMethod.getName().getIdentifier())
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Hover provideHover(ASTNode node, TypeDeclaration typeDeclaration, ITypeBinding type, int offset,
|
||||
TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Range> getLiveHoverHints(TypeDeclaration typeDeclaration, TextDocument doc,
|
||||
SpringBootApp[] runningApps) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,189 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2018 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.livehover;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.MarkerAnnotation;
|
||||
import org.eclipse.jdt.core.dom.MethodDeclaration;
|
||||
import org.eclipse.jdt.core.dom.TypeDeclaration;
|
||||
import org.eclipse.lsp4j.Hover;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.eclipse.lsp4j.jsonrpc.messages.Either;
|
||||
import org.springframework.ide.vscode.boot.java.Annotations;
|
||||
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
|
||||
import org.springframework.ide.vscode.boot.java.utils.FunctionUtils;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBeansModel;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion;
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
import org.springframework.ide.vscode.commons.util.StringUtil;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import reactor.util.function.Tuple3;
|
||||
|
||||
public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverProvider {
|
||||
|
||||
@Override
|
||||
protected void addAutomaticallyWiredContructor(StringBuilder hover, Annotation annotation, LiveBeansModel beans, LiveBean bean, IJavaProject project) {
|
||||
TypeDeclaration typeDecl = ASTUtils.findDeclaringType(annotation);
|
||||
if (typeDecl != null) {
|
||||
MethodDeclaration[] constructors = ASTUtils.findConstructors(typeDecl);
|
||||
|
||||
if (constructors != null && constructors.length == 1 && !hasAutowiredAnnotation(constructors[0])) {
|
||||
String[] dependencies = bean.getDependencies();
|
||||
|
||||
if (dependencies != null && dependencies.length > 0) {
|
||||
hover.append("\n\n");
|
||||
hover.append(LiveHoverUtils.showBean(bean) + " got autowired with:\n\n");
|
||||
|
||||
boolean firstDependency = true;
|
||||
for (String injectedBean : dependencies) {
|
||||
if (!firstDependency) {
|
||||
hover.append("\n");
|
||||
}
|
||||
List<LiveBean> dependencyBeans = beans.getBeansOfName(injectedBean);
|
||||
for (LiveBean dependencyBean : dependencyBeans) {
|
||||
hover.append("- " + LiveHoverUtils.showBeanWithResource(dependencyBean, " ", project));
|
||||
}
|
||||
firstDependency = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasAutowiredAnnotation(MethodDeclaration constructor) {
|
||||
List<?> modifiers = constructor.modifiers();
|
||||
for (Object modifier : modifiers) {
|
||||
if (modifier instanceof MarkerAnnotation) {
|
||||
ITypeBinding typeBinding = ((MarkerAnnotation) modifier).resolveTypeBinding();
|
||||
if (typeBinding != null && typeBinding.getQualifiedName().equals(Annotations.AUTOWIRED)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LiveBean getDefinedBean(Annotation annotation) {
|
||||
return getDefinedBeanForComponent(annotation);
|
||||
}
|
||||
|
||||
public static LiveBean getDefinedBeanForComponent(Annotation annotation) {
|
||||
//Move to ASTUtils?
|
||||
TypeDeclaration declaringType = ASTUtils.getAnnotatedType(annotation);
|
||||
return getDefinedBeanForType(declaringType, annotation);
|
||||
}
|
||||
|
||||
private static LiveBean getDefinedBeanForType(TypeDeclaration declaringType, Annotation annotation) {
|
||||
if (declaringType != null) {
|
||||
ITypeBinding beanType = declaringType.resolveBinding();
|
||||
if (beanType != null) {
|
||||
String id = getBeanId(annotation, beanType);
|
||||
if (StringUtil.hasText(id)) {
|
||||
return LiveBean.builder().id(id).type(beanType.getQualifiedName()).build();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String getBeanId(Annotation annotation, ITypeBinding beanType) {
|
||||
return ASTUtils.getAttribute(annotation, "value").flatMap(ASTUtils::getFirstString)
|
||||
.orElseGet(() -> {
|
||||
String typeName = beanType.getName();
|
||||
|
||||
ITypeBinding declaringClass = beanType.getDeclaringClass();
|
||||
while (declaringClass != null) {
|
||||
typeName = declaringClass.getName() + "." + typeName;
|
||||
declaringClass = declaringClass.getDeclaringClass();
|
||||
}
|
||||
|
||||
if (StringUtil.hasText(typeName)) {
|
||||
return Character.toLowerCase(typeName.charAt(0)) + typeName.substring(1);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Range> getLiveHoverHints(TypeDeclaration typeDeclaration, TextDocument doc,
|
||||
SpringBootApp[] runningApps) {
|
||||
Tuple3<String, String, DocumentRegion> functionBean = FunctionUtils.getFunctionBean(typeDeclaration, doc);
|
||||
if (functionBean != null && runningApps.length > 0) {
|
||||
try {
|
||||
LiveBean definedBean = getDefinedBeanForType(typeDeclaration, null);
|
||||
if (definedBean != null) {
|
||||
if (Stream.of(runningApps).anyMatch(app -> LiveHoverUtils.hasRelevantBeans(app, definedBean))) {
|
||||
Optional<Range> nameRange = Optional.of(ASTUtils.nodeRegion(doc, typeDeclaration.getName()).asRange());
|
||||
if (nameRange.isPresent()) {
|
||||
return ImmutableList.of(nameRange.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
}
|
||||
}
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Hover provideHover(ASTNode node, TypeDeclaration typeDeclaration, ITypeBinding type, int offset,
|
||||
TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) {
|
||||
Tuple3<String, String, DocumentRegion> functionBean = FunctionUtils.getFunctionBean(typeDeclaration, doc);
|
||||
if (functionBean != null && runningApps.length > 0) {
|
||||
|
||||
LiveBean definedBean = getDefinedBeanForType(typeDeclaration, null);
|
||||
if (definedBean != null) {
|
||||
StringBuilder hover = new StringBuilder();
|
||||
hover.append("**Injection report for " + LiveHoverUtils.showBean(definedBean) + "**\n\n");
|
||||
|
||||
boolean hasInterestingApp = false;
|
||||
for (SpringBootApp app : runningApps) {
|
||||
LiveBeansModel beans = app.getBeans();
|
||||
List<LiveBean> relevantBeans = LiveHoverUtils.findRelevantBeans(app, definedBean).collect(Collectors.toList());
|
||||
|
||||
if (!relevantBeans.isEmpty()) {
|
||||
if (!hasInterestingApp) {
|
||||
hasInterestingApp = true;
|
||||
} else {
|
||||
hover.append("\n\n");
|
||||
}
|
||||
hover.append(LiveHoverUtils.niceAppName(app) + ":");
|
||||
|
||||
for (LiveBean bean : relevantBeans) {
|
||||
addInjectedInto(definedBean, hover, beans, bean, project);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasInterestingApp) {
|
||||
return new Hover(ImmutableList.of(Either.forLeft(hover.toString())));
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2018 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.livehover;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.ide.vscode.boot.java.utils.SourceLinks;
|
||||
import org.springframework.ide.vscode.boot.java.utils.SpringResource;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBeansModel;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.util.Renderables;
|
||||
import org.springframework.ide.vscode.commons.util.StringUtil;
|
||||
|
||||
public class LiveHoverUtils {
|
||||
|
||||
public static String showBean(LiveBean bean) {
|
||||
StringBuilder buf = new StringBuilder("Bean [id: " + bean.getId());
|
||||
String type = bean.getType(true);
|
||||
if (type != null) {
|
||||
buf.append(", type: `" + type + "`");
|
||||
}
|
||||
buf.append(']');
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public static String showBeanWithResource(LiveBean bean, String indentStr, IJavaProject project) {
|
||||
String newline = " \n"+indentStr; //Note: the double space before newline makes markdown see it as a real line break
|
||||
|
||||
String type = bean.getType(true);
|
||||
|
||||
StringBuilder buf = new StringBuilder("Bean: ");
|
||||
buf.append(bean.getId());
|
||||
if (type != null) {
|
||||
// Try creating a URL link to open source for the type
|
||||
buf.append(newline);
|
||||
buf.append("Type: ");
|
||||
Optional<String> url = SourceLinks.sourceLinkUrlForFQName(project, type);
|
||||
if (url.isPresent()) {
|
||||
buf.append(Renderables.link(type, url.get()).toMarkdown());
|
||||
} else {
|
||||
buf.append("`" + type + "`");
|
||||
}
|
||||
}
|
||||
String resource = bean.getResource();
|
||||
if (StringUtil.hasText(resource)) {
|
||||
buf.append(newline);
|
||||
buf.append("Resource: ");
|
||||
buf.append(showResource(resource, project));
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public static String showResource(String resource, IJavaProject project) {
|
||||
return new SpringResource(resource, project).toMarkdown();
|
||||
}
|
||||
|
||||
public static String niceAppName(SpringBootApp app) {
|
||||
return niceAppName(app.getProcessID() ,app.getProcessName());
|
||||
}
|
||||
|
||||
public static String niceAppName(String processId, String processName) {
|
||||
return "Process [PID=" + processId + ", name=`" + processName + "`]";
|
||||
}
|
||||
|
||||
public static boolean hasRelevantBeans(SpringBootApp app, LiveBean definedBean) {
|
||||
return findRelevantBeans(app, definedBean).findAny().isPresent();
|
||||
}
|
||||
|
||||
public static Stream<LiveBean> findRelevantBeans(SpringBootApp app, LiveBean definedBean) {
|
||||
LiveBeansModel beansModel = app.getBeans();
|
||||
if (beansModel != null) {
|
||||
Stream<LiveBean> relevantBeans = beansModel.getBeansOfName(definedBean.getId()).stream();
|
||||
String type = definedBean.getType();
|
||||
if (type != null) {
|
||||
relevantBeans = relevantBeans.filter(bean -> type.equals(bean.getType(true)));
|
||||
}
|
||||
return relevantBeans;
|
||||
}
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016, 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
|
||||
package org.springframework.ide.vscode.boot.java.metadata;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
||||
import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.ProgressService;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver;
|
||||
import org.springframework.ide.vscode.commons.util.FuzzyMap;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
|
||||
public class DefaultSpringPropertyIndexProvider implements SpringPropertyIndexProvider {
|
||||
|
||||
private static final FuzzyMap<ConfigurationMetadataProperty> EMPTY_INDEX = new SpringPropertyIndex(null);
|
||||
|
||||
private JavaProjectFinder javaProjectFinder;
|
||||
private SpringPropertiesIndexManager indexManager;
|
||||
|
||||
private ProgressService progressService = (id, msg) -> {
|
||||
/* ignore */ };
|
||||
|
||||
public DefaultSpringPropertyIndexProvider(JavaProjectFinder javaProjectFinder, ProjectObserver projectObserver) {
|
||||
this.javaProjectFinder = javaProjectFinder;
|
||||
this.indexManager = new SpringPropertiesIndexManager(projectObserver);
|
||||
}
|
||||
|
||||
public DefaultSpringPropertyIndexProvider(JavaProjectFinder javaProjectFinder) {
|
||||
this(javaProjectFinder, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FuzzyMap<ConfigurationMetadataProperty> getIndex(IDocument doc) {
|
||||
Optional<IJavaProject> jp = javaProjectFinder.find(new TextDocumentIdentifier(doc.getUri()));
|
||||
if (jp.isPresent()) {
|
||||
return indexManager.get(jp.get(), progressService);
|
||||
}
|
||||
return EMPTY_INDEX;
|
||||
}
|
||||
|
||||
public void setProgressService(ProgressService progressService) {
|
||||
this.progressService = progressService;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016, 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
|
||||
package org.springframework.ide.vscode.boot.java.metadata;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.zip.ZipEntry;
|
||||
|
||||
import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepository;
|
||||
import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepositoryJsonBuilder;
|
||||
import org.springframework.ide.vscode.commons.java.IClasspath;
|
||||
|
||||
public class PropertiesLoader {
|
||||
|
||||
private static final String MAIN_SPRING_CONFIGURATION_METADATA_JSON = "META-INF/spring-configuration-metadata.json";
|
||||
|
||||
public static final String ADDITIONAL_SPRING_CONFIGURATION_METADATA_JSON = "META-INF/additional-spring-configuration-metadata.json";
|
||||
|
||||
/**
|
||||
* The default classpath location for config metadata loaded when scanning .jar files on the classpath.
|
||||
*/
|
||||
public static final String[] JAR_META_DATA_LOCATIONS = {
|
||||
MAIN_SPRING_CONFIGURATION_METADATA_JSON
|
||||
//Not scanning 'additional' metadata because it integrated already in the main data.
|
||||
};
|
||||
|
||||
/**
|
||||
* The default classpath location for config metadata loaded when scanning project output folders.
|
||||
*/
|
||||
public static final String[] PROJECT_META_DATA_LOCATIONS = {
|
||||
MAIN_SPRING_CONFIGURATION_METADATA_JSON,
|
||||
ADDITIONAL_SPRING_CONFIGURATION_METADATA_JSON
|
||||
};
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(PropertiesLoader.class.getName());
|
||||
|
||||
private ConfigurationMetadataRepositoryJsonBuilder builder = ConfigurationMetadataRepositoryJsonBuilder.create();
|
||||
|
||||
public ConfigurationMetadataRepository load(IClasspath classPath) {
|
||||
try {
|
||||
classPath.getClasspathEntries().forEach(entry -> {
|
||||
File fileEntry = entry.toFile();
|
||||
if (fileEntry.exists()) {
|
||||
if (fileEntry.isDirectory()) {
|
||||
loadFromOutputFolder(entry);
|
||||
} else {
|
||||
loadFromJar(entry);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
LOG.log(Level.SEVERE, "Failed to retrieve classpath", e);
|
||||
}
|
||||
ConfigurationMetadataRepository repository = builder.build();
|
||||
return repository;
|
||||
}
|
||||
|
||||
private void loadFromOutputFolder(Path outputFolderPath) {
|
||||
if (outputFolderPath != null && Files.exists(outputFolderPath)) {
|
||||
Arrays.stream(PROJECT_META_DATA_LOCATIONS).forEach(mdLoc -> {
|
||||
loadFromJsonFile(outputFolderPath.resolve(mdLoc));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void loadFromJsonFile(Path mdf) {
|
||||
if (Files.exists(mdf)) {
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = Files.newInputStream(mdf);
|
||||
loadFromInputStream(is);
|
||||
} catch (Exception e) {
|
||||
LOG.log(Level.SEVERE, "Error loading file '" + mdf + "'", e);
|
||||
} finally {
|
||||
if (is!=null) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadFromJar(Path f) {
|
||||
JarFile jarFile = null;
|
||||
try {
|
||||
jarFile = new JarFile(f.toFile());
|
||||
//jarDump(jarFile);
|
||||
for (String loc : JAR_META_DATA_LOCATIONS) {
|
||||
ZipEntry e = jarFile.getEntry(loc);
|
||||
if (e!=null) {
|
||||
loadFrom(jarFile, e);
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
LOG.log(Level.SEVERE, "Error loading JAR file", e);
|
||||
} finally {
|
||||
if (jarFile!=null) {
|
||||
try {
|
||||
jarFile.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void loadFrom(JarFile jarFile, ZipEntry ze) {
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = jarFile.getInputStream(ze);
|
||||
loadFromInputStream(is);
|
||||
} catch (Throwable e) {
|
||||
LOG.log(Level.SEVERE, "Error loading JAR file", e);
|
||||
} finally {
|
||||
if (is!=null) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadFromInputStream(InputStream is) throws IOException {
|
||||
builder.withJsonResource(is);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2014, 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.metadata;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.ProgressService;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver.Listener;
|
||||
import org.springframework.ide.vscode.commons.util.FuzzyMap;
|
||||
|
||||
/**
|
||||
* Support for Reconciling, Content Assist and Hover Text in spring properties
|
||||
* file all make use of a per-project index of spring properties metadata extracted
|
||||
* from project's classpath. This Index manager is responsible for keeping at most
|
||||
* one index per-project and to keep the index up-to-date.
|
||||
*
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
public class SpringPropertiesIndexManager {
|
||||
|
||||
private Map<IJavaProject, SpringPropertyIndex> indexes = null;
|
||||
private static int progressIdCt = 0;
|
||||
|
||||
public SpringPropertiesIndexManager(ProjectObserver projectObserver) {
|
||||
if (projectObserver != null) {
|
||||
projectObserver.addListener(new Listener() {
|
||||
|
||||
@Override
|
||||
public void created(IJavaProject project) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changed(IJavaProject project) {
|
||||
invalidateIndex(project);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleted(IJavaProject project) {
|
||||
invalidateIndex(project);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized SpringPropertyIndex invalidateIndex(IJavaProject project) {
|
||||
if (indexes != null) {
|
||||
return indexes.remove(project);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public synchronized FuzzyMap<ConfigurationMetadataProperty> get(IJavaProject project, ProgressService progressService) {
|
||||
if (indexes==null) {
|
||||
indexes = new HashMap<>();
|
||||
}
|
||||
|
||||
SpringPropertyIndex index = indexes.get(project);
|
||||
if (index==null) {
|
||||
String progressId = getProgressId();
|
||||
if (progressService != null) {
|
||||
progressService.progressEvent(progressId, "Indexing Spring Boot Properties...");
|
||||
}
|
||||
|
||||
index = new SpringPropertyIndex(project.getClasspath());
|
||||
indexes.put(project, index);
|
||||
|
||||
if (progressService != null) {
|
||||
progressService.progressEvent(progressId, null);
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
private static synchronized String getProgressId() {
|
||||
return DefaultSpringPropertyIndexProvider.class.getName()+ (progressIdCt++);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2015, 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.metadata;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty;
|
||||
import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepository;
|
||||
import org.springframework.ide.vscode.commons.java.IClasspath;
|
||||
import org.springframework.ide.vscode.commons.util.FuzzyMap;
|
||||
|
||||
public class SpringPropertyIndex extends FuzzyMap<ConfigurationMetadataProperty> {
|
||||
|
||||
public SpringPropertyIndex(IClasspath projectPath) {
|
||||
if (projectPath!=null) {
|
||||
PropertiesLoader loader = new PropertiesLoader();
|
||||
ConfigurationMetadataRepository metadata = loader.load(projectPath);
|
||||
Collection<ConfigurationMetadataProperty> allEntries = metadata.getAllProperties().values();
|
||||
for (ConfigurationMetadataProperty item : allEntries) {
|
||||
add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getKey(ConfigurationMetadataProperty entry) {
|
||||
return entry.getId();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2015, 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.metadata;
|
||||
|
||||
import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty;
|
||||
import org.springframework.ide.vscode.commons.util.FuzzyMap;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
|
||||
|
||||
@FunctionalInterface
|
||||
public interface SpringPropertyIndexProvider {
|
||||
FuzzyMap<ConfigurationMetadataProperty> getIndex(IDocument doc);
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.requestmapping;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.lsp4j.Location;
|
||||
import org.eclipse.lsp4j.Position;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.eclipse.lsp4j.SymbolInformation;
|
||||
import org.eclipse.lsp4j.SymbolKind;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.RunningAppProvider;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class LiveAppURLSymbolProvider {
|
||||
|
||||
private final RunningAppProvider runningAppProvider;
|
||||
|
||||
public LiveAppURLSymbolProvider(RunningAppProvider runningAppProvider) {
|
||||
this.runningAppProvider = runningAppProvider;
|
||||
}
|
||||
|
||||
public List<? extends SymbolInformation> getSymbols(String query) {
|
||||
System.out.println(query);
|
||||
|
||||
List<SymbolInformation> result = new ArrayList<>();
|
||||
|
||||
try {
|
||||
SpringBootApp[] runningApps = runningAppProvider.getAllRunningSpringApps().toArray(new SpringBootApp[0]);
|
||||
for (SpringBootApp app : runningApps) {
|
||||
try {
|
||||
String host = app.getHost();
|
||||
String port = app.getPort();
|
||||
Stream<String> urls = app.getRequestMappings().stream()
|
||||
.flatMap(rm -> Arrays.stream(rm.getSplitPath()))
|
||||
.map(path -> UrlUtil.createUrl(host, port, path));
|
||||
urls.forEach(url -> result.add(new SymbolInformation(url, SymbolKind.Method, new Location(url, new Range(new Position(0, 0), new Position(0, 1))))));
|
||||
}
|
||||
catch (Exception e) {
|
||||
Log.log(e);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.requestmapping;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class RequestMappingCompletionProcessor {
|
||||
|
||||
}
|
||||
@@ -1,195 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2018 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.requestmapping;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.IMethodBinding;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.MethodDeclaration;
|
||||
import org.eclipse.jdt.core.dom.TypeDeclaration;
|
||||
import org.eclipse.lsp4j.Hover;
|
||||
import org.eclipse.lsp4j.MarkedString;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.eclipse.lsp4j.jsonrpc.messages.Either;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.HoverProvider;
|
||||
import org.springframework.ide.vscode.boot.java.livehover.LiveHoverUtils;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.requestmappings.RequestMapping;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.util.BadLocationException;
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
import org.springframework.ide.vscode.commons.util.Renderable;
|
||||
import org.springframework.ide.vscode.commons.util.Renderables;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import reactor.util.function.Tuple2;
|
||||
import reactor.util.function.Tuples;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class RequestMappingHoverProvider implements HoverProvider {
|
||||
|
||||
@Override
|
||||
public Hover provideHover(ASTNode node, Annotation annotation,
|
||||
ITypeBinding type, int offset, TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) {
|
||||
return provideHover(annotation, doc, runningApps);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Range> getLiveHoverHints(Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) {
|
||||
try {
|
||||
if (runningApps.length > 0) {
|
||||
List<Tuple2<RequestMapping, SpringBootApp>> val = getRequestMappingMethodFromRunningApp(annotation, runningApps);
|
||||
if (!val.isEmpty()) {
|
||||
Range hoverRange = doc.toRange(annotation.getStartPosition(), annotation.getLength());
|
||||
return ImmutableList.of(hoverRange);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (BadLocationException e) {
|
||||
Log.log(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Hover provideHover(Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) {
|
||||
|
||||
try {
|
||||
List<Either<String, MarkedString>> hoverContent = new ArrayList<>();
|
||||
|
||||
List<Tuple2<RequestMapping, SpringBootApp>> val = getRequestMappingMethodFromRunningApp(annotation, runningApps);
|
||||
|
||||
if (!val.isEmpty()) {
|
||||
addHoverContent(val, hoverContent);
|
||||
}
|
||||
|
||||
Range hoverRange = doc.toRange(annotation.getStartPosition(), annotation.getLength());
|
||||
Hover hover = new Hover();
|
||||
|
||||
hover.setContents(hoverContent);
|
||||
hover.setRange(hoverRange);
|
||||
|
||||
return hover;
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<Tuple2<RequestMapping, SpringBootApp>> getRequestMappingMethodFromRunningApp(Annotation annotation,
|
||||
SpringBootApp[] runningApps) {
|
||||
|
||||
List<Tuple2<RequestMapping, SpringBootApp>> results = new ArrayList<>();
|
||||
try {
|
||||
for (SpringBootApp app : runningApps) {
|
||||
Collection<RequestMapping> mappings = app.getRequestMappings();
|
||||
if (mappings != null && !mappings.isEmpty()) {
|
||||
mappings.stream()
|
||||
.filter(rm -> methodMatchesAnnotation(annotation, rm))
|
||||
.map(rm -> Tuples.of(rm, app))
|
||||
.findFirst().ifPresent(t -> results.add(t));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private boolean methodMatchesAnnotation(Annotation annotation, RequestMapping rm) {
|
||||
String rqClassName = rm.getFullyQualifiedClassName();
|
||||
|
||||
if (rqClassName != null) {
|
||||
int chop = rqClassName.indexOf("$$EnhancerBySpringCGLIB$$");
|
||||
if (chop >= 0) {
|
||||
rqClassName = rqClassName.substring(0, chop);
|
||||
}
|
||||
|
||||
rqClassName = rqClassName.replace('$', '.');
|
||||
|
||||
ASTNode parent = annotation.getParent();
|
||||
if (parent instanceof MethodDeclaration) {
|
||||
MethodDeclaration methodDec = (MethodDeclaration) parent;
|
||||
IMethodBinding binding = methodDec.resolveBinding();
|
||||
return binding.getDeclaringClass().getQualifiedName().equals(rqClassName)
|
||||
&& binding.getName().equals(rm.getMethodName())
|
||||
&& Arrays.equals(Arrays.stream(binding.getParameterTypes())
|
||||
.map(t -> t.getTypeDeclaration().getQualifiedName())
|
||||
.toArray(String[]::new),
|
||||
rm.getMethodParameters());
|
||||
// } else if (parent instanceof TypeDeclaration) {
|
||||
// TypeDeclaration typeDec = (TypeDeclaration) parent;
|
||||
// return typeDec.resolveBinding().getQualifiedName().equals(rqClassName);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void addHoverContent(List<Tuple2<RequestMapping, SpringBootApp>> mappingMethods, List<Either<String, MarkedString>> hoverContent) throws Exception {
|
||||
for (int i = 0; i < mappingMethods.size(); i++) {
|
||||
Tuple2<RequestMapping, SpringBootApp> mappingMethod = mappingMethods.get(i);
|
||||
|
||||
SpringBootApp app = mappingMethod.getT2();
|
||||
String port = mappingMethod.getT2().getPort();
|
||||
String host = mappingMethod.getT2().getHost();
|
||||
|
||||
List<Renderable> renderableUrls = Arrays.stream(mappingMethod.getT1().getSplitPath()).flatMap(path -> {
|
||||
String url = UrlUtil.createUrl(host, port, path);
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("[");
|
||||
builder.append(url);
|
||||
builder.append("]");
|
||||
builder.append("(");
|
||||
builder.append(url);
|
||||
builder.append(")");
|
||||
return Stream.of(Renderables.text(builder.toString()), Renderables.lineBreak());
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// Remove the last line break
|
||||
renderableUrls.remove(renderableUrls.size() - 1);
|
||||
|
||||
hoverContent.add(Either.forLeft(Renderables.concat(renderableUrls).toMarkdown()));
|
||||
hoverContent.add(Either.forLeft(LiveHoverUtils.niceAppName(app)));
|
||||
if (i < mappingMethods.size() - 1) {
|
||||
// Three dashes == line separator in Markdown
|
||||
hoverContent.add(Either.forLeft("---"));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Hover provideHover(ASTNode node, TypeDeclaration typeDeclaration, ITypeBinding type, int offset,
|
||||
TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Range> getLiveHoverHints(TypeDeclaration typeDeclaration, TextDocument doc,
|
||||
SpringBootApp[] runningApps) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,184 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2018 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.requestmapping;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.Expression;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.MemberValuePair;
|
||||
import org.eclipse.jdt.core.dom.MethodDeclaration;
|
||||
import org.eclipse.jdt.core.dom.NormalAnnotation;
|
||||
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
|
||||
import org.eclipse.jdt.core.dom.TypeDeclaration;
|
||||
import org.eclipse.lsp4j.Location;
|
||||
import org.eclipse.lsp4j.SymbolInformation;
|
||||
import org.eclipse.lsp4j.SymbolKind;
|
||||
import org.springframework.ide.vscode.boot.java.Annotations;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider;
|
||||
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class RequestMappingSymbolProvider implements SymbolProvider {
|
||||
|
||||
@Override
|
||||
public Collection<SymbolInformation> getSymbols(Annotation node, ITypeBinding annotationType, Collection<ITypeBinding> metaAnnotations, TextDocument doc) {
|
||||
if (node.getParent() instanceof MethodDeclaration) {
|
||||
try {
|
||||
Location location = new Location(doc.getUri(), doc.toRange(node.getStartPosition(), node.getLength()));
|
||||
String[] path = getPath(node);
|
||||
String[] parentPath = getParentPath(node);
|
||||
String[] method = getMethod(node);
|
||||
|
||||
String methodStr = method == null || method.length == 0 ? "" : String.join(",", method);
|
||||
|
||||
return (parentPath == null ? Stream.of("") : Arrays.stream(parentPath)).filter(Objects::nonNull)
|
||||
.flatMap(parent -> (path == null ? Stream.<String>empty() : Arrays.stream(path))
|
||||
.filter(Objects::nonNull).map(p -> {
|
||||
String separator = !parent.endsWith("/") && !p.startsWith("/") ? "/" : "";
|
||||
String resultPath = parent + separator + p;
|
||||
if (resultPath.endsWith("/")) {
|
||||
resultPath = resultPath.substring(0, resultPath.length() - 1);
|
||||
}
|
||||
return resultPath.startsWith("/") ? resultPath : "/" + resultPath;
|
||||
}))
|
||||
.map(p -> "@" + p + (methodStr.isEmpty() ? "" : " -- " + methodStr))
|
||||
.map(symbolLabel -> new SymbolInformation(symbolLabel, SymbolKind.Interface, location))
|
||||
.collect(Collectors.toList());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String[] getMethod(Annotation node) {
|
||||
String[] methods = null;
|
||||
|
||||
if (node.isNormalAnnotation()) {
|
||||
NormalAnnotation normNode = (NormalAnnotation) node;
|
||||
List<?> values = normNode.values();
|
||||
for (Iterator<?> iterator = values.iterator(); iterator.hasNext();) {
|
||||
Object object = iterator.next();
|
||||
if (object instanceof MemberValuePair) {
|
||||
MemberValuePair pair = (MemberValuePair) object;
|
||||
String valueName = pair.getName().getIdentifier();
|
||||
if (valueName != null && valueName.equals("method")) {
|
||||
Expression expression = pair.getValue();
|
||||
methods = ASTUtils.getExpressionValueAsArray(expression);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (node instanceof SingleMemberAnnotation) {
|
||||
methods = getRequestMethod((SingleMemberAnnotation)node);
|
||||
}
|
||||
|
||||
if (methods == null && node.getParent() instanceof MethodDeclaration) {
|
||||
Annotation parentAnnotation = getParentAnnotation(node);
|
||||
if (parentAnnotation != null) {
|
||||
methods = getMethod(parentAnnotation);
|
||||
}
|
||||
}
|
||||
|
||||
return methods;
|
||||
}
|
||||
|
||||
private String[] getPath(Annotation node) {
|
||||
if (node.isNormalAnnotation()) {
|
||||
NormalAnnotation normNode = (NormalAnnotation) node;
|
||||
List<?> values = normNode.values();
|
||||
for (Iterator<?> iterator = values.iterator(); iterator.hasNext();) {
|
||||
Object object = iterator.next();
|
||||
if (object instanceof MemberValuePair) {
|
||||
MemberValuePair pair = (MemberValuePair) object;
|
||||
String valueName = pair.getName().getIdentifier();
|
||||
if (valueName != null && (valueName.equals("value") || valueName.equals("path"))) {
|
||||
Expression expression = pair.getValue();
|
||||
return ASTUtils.getExpressionValueAsArray(expression);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (node.isSingleMemberAnnotation()) {
|
||||
SingleMemberAnnotation singleNode = (SingleMemberAnnotation) node;
|
||||
Expression expression = singleNode.getValue();
|
||||
return ASTUtils.getExpressionValueAsArray(expression);
|
||||
}
|
||||
|
||||
return new String[] { "" };
|
||||
}
|
||||
|
||||
private String[] getParentPath(Annotation node) {
|
||||
Annotation parentAnnotation = getParentAnnotation(node);
|
||||
return parentAnnotation == null ? null : getPath(parentAnnotation);
|
||||
}
|
||||
|
||||
private Annotation getParentAnnotation(Annotation node) {
|
||||
ASTNode parent = node.getParent() != null ? node.getParent().getParent() : null;
|
||||
while (parent != null && !(parent instanceof TypeDeclaration)) {
|
||||
parent = parent.getParent();
|
||||
}
|
||||
|
||||
if (parent != null) {
|
||||
TypeDeclaration type = (TypeDeclaration) parent;
|
||||
List<?> modifiers = type.modifiers();
|
||||
Iterator<?> iterator = modifiers.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Object modifier = iterator.next();
|
||||
if (modifier instanceof Annotation) {
|
||||
Annotation annotation = (Annotation) modifier;
|
||||
ITypeBinding resolvedType = annotation.resolveTypeBinding();
|
||||
String annotationType = resolvedType.getQualifiedName();
|
||||
if (annotationType != null && Annotations.SPRING_REQUEST_MAPPING.equals(annotationType)) {
|
||||
return annotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String[] getRequestMethod(SingleMemberAnnotation annotation) {
|
||||
ITypeBinding type = annotation.resolveTypeBinding();
|
||||
if (type != null) {
|
||||
switch (type.getQualifiedName()) {
|
||||
case Annotations.SPRING_GET_MAPPING:
|
||||
return new String[] { "GET" };
|
||||
case Annotations.SPRING_POST_MAPPING:
|
||||
return new String[] { "POST" };
|
||||
case Annotations.SPRING_DELETE_MAPPING:
|
||||
return new String[] { "DELETE" };
|
||||
case Annotations.SPRING_PUT_MAPPING:
|
||||
return new String[] { "PUT" };
|
||||
case Annotations.SPRING_PATCH_MAPPING:
|
||||
return new String[] { "PATCH" };
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<SymbolInformation> getSymbols(TypeDeclaration typeDeclaration, TextDocument doc) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.requestmapping;
|
||||
|
||||
public class UrlUtil {
|
||||
|
||||
/**
|
||||
* Creates http URL string based on host, port and path
|
||||
* @param host
|
||||
* @param port
|
||||
* @param path
|
||||
* @return the resultant URL
|
||||
*/
|
||||
public static String createUrl(String host, String port, String path) {
|
||||
if (path==null) {
|
||||
path = "";
|
||||
}
|
||||
if (host!=null) {
|
||||
if (port != null) {
|
||||
if (!path.startsWith("/")) {
|
||||
path = "/" +path;
|
||||
}
|
||||
return "http://"+host+":"+port+path;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.scope;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class Constants {
|
||||
|
||||
public static final String SPRING_SCOPE = "org.springframework.context.annotation.Scope";
|
||||
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.scope;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.MemberValuePair;
|
||||
import org.eclipse.jdt.core.dom.SimpleName;
|
||||
import org.eclipse.jdt.core.dom.StringLiteral;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.CompletionProvider;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class ScopeCompletionProcessor implements CompletionProvider {
|
||||
|
||||
@Override
|
||||
public Collection<ICompletionProposal> provideCompletions(ASTNode node, Annotation annotation, ITypeBinding type,
|
||||
int offset, IDocument doc) {
|
||||
|
||||
List<ICompletionProposal> result = new ArrayList<>();
|
||||
|
||||
try {
|
||||
if (node instanceof SimpleName && node.getParent() instanceof MemberValuePair) {
|
||||
MemberValuePair memberPair = (MemberValuePair) node.getParent();
|
||||
|
||||
// case: @Scope(value=<*>)
|
||||
if ("value".equals(memberPair.getName().toString()) && memberPair.getValue().toString().equals("$missing$")) {
|
||||
for (ScopeNameCompletion completion : ScopeNameCompletionProposal.COMPLETIONS) {
|
||||
ICompletionProposal proposal = new ScopeNameCompletionProposal(completion, doc, offset, offset, "");
|
||||
result.add(proposal);
|
||||
}
|
||||
}
|
||||
}
|
||||
// case: @Scope(<*>)
|
||||
else if (node == annotation && doc.get(offset - 1, 2).endsWith("()")) {
|
||||
for (ScopeNameCompletion completion : ScopeNameCompletionProposal.COMPLETIONS) {
|
||||
ICompletionProposal proposal = new ScopeNameCompletionProposal(completion, doc, offset, offset, "");
|
||||
result.add(proposal);
|
||||
}
|
||||
}
|
||||
else if (node instanceof StringLiteral && node.getParent() instanceof Annotation) {
|
||||
// case: @Scope("...")
|
||||
if (node.toString().startsWith("\"") && node.toString().endsWith("\"")) {
|
||||
String prefix = doc.get(node.getStartPosition(), offset - node.getStartPosition());
|
||||
for (ScopeNameCompletion completion : ScopeNameCompletionProposal.COMPLETIONS) {
|
||||
if (completion.getValue().startsWith(prefix)) {
|
||||
ICompletionProposal proposal = new ScopeNameCompletionProposal(completion, doc, node.getStartPosition(), node.getStartPosition() + node.getLength(), prefix);
|
||||
result.add(proposal);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (node instanceof StringLiteral && node.getParent() instanceof MemberValuePair) {
|
||||
MemberValuePair memberPair = (MemberValuePair) node.getParent();
|
||||
|
||||
// case: @Scope(value=<*>)
|
||||
if ("value".equals(memberPair.getName().toString()) && node.toString().startsWith("\"") && node.toString().endsWith("\"")) {
|
||||
String prefix = doc.get(node.getStartPosition(), offset - node.getStartPosition());
|
||||
for (ScopeNameCompletion completion : ScopeNameCompletionProposal.COMPLETIONS) {
|
||||
if (completion.getValue().startsWith(prefix)) {
|
||||
ICompletionProposal proposal = new ScopeNameCompletionProposal(completion, doc, node.getStartPosition(), node.getStartPosition() + node.getLength(), prefix);
|
||||
result.add(proposal);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.scope;
|
||||
|
||||
import org.eclipse.lsp4j.CompletionItemKind;
|
||||
import org.springframework.ide.vscode.commons.util.Renderable;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class ScopeNameCompletion {
|
||||
|
||||
private final String label;
|
||||
private final String detail;
|
||||
private final Renderable documentation;
|
||||
private final CompletionItemKind kind;
|
||||
private final String value;
|
||||
|
||||
public ScopeNameCompletion(String value, String label, String detail, Renderable documentation, CompletionItemKind kind) {
|
||||
super();
|
||||
this.value = value;
|
||||
this.label = label;
|
||||
this.detail = detail;
|
||||
this.documentation = documentation;
|
||||
this.kind = kind;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public String getDetail() {
|
||||
return detail;
|
||||
}
|
||||
|
||||
public CompletionItemKind getKind() {
|
||||
return kind;
|
||||
}
|
||||
|
||||
public Renderable getDocumentation() {
|
||||
return documentation;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.scope;
|
||||
|
||||
import org.eclipse.lsp4j.CompletionItemKind;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
|
||||
import org.springframework.ide.vscode.commons.util.Renderable;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class ScopeNameCompletionProposal implements ICompletionProposal {
|
||||
|
||||
public static final ScopeNameCompletion[] COMPLETIONS = new ScopeNameCompletion[] {
|
||||
new ScopeNameCompletion("\"prototype\"", "prototype", "prototype scope", null, CompletionItemKind.Value),
|
||||
new ScopeNameCompletion("\"singleton\"", "singleton", "singleton scope (default)", null, CompletionItemKind.Value),
|
||||
new ScopeNameCompletion("\"request\"", "request", "request scope", null, CompletionItemKind.Value),
|
||||
new ScopeNameCompletion("\"session\"", "session", "session scope", null, CompletionItemKind.Value),
|
||||
new ScopeNameCompletion("\"globalSession\"", "globalSession", "globalSession scope", null, CompletionItemKind.Value),
|
||||
new ScopeNameCompletion("\"application\"", "application", "application scope", null, CompletionItemKind.Value),
|
||||
new ScopeNameCompletion("\"websocket\"", "websocket", "websocket scope", null, CompletionItemKind.Value)
|
||||
};
|
||||
|
||||
private final IDocument doc;
|
||||
private final int startOffset;
|
||||
private final int endOffset;
|
||||
private final ScopeNameCompletion completion;
|
||||
private final String prefix;
|
||||
|
||||
public ScopeNameCompletionProposal(ScopeNameCompletion completion, IDocument doc, int startOffset, int endOffset, String prefix) {
|
||||
this.completion = completion;
|
||||
this.doc = doc;
|
||||
this.startOffset = startOffset;
|
||||
this.endOffset = endOffset;
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return completion.getLabel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletionItemKind getKind() {
|
||||
return completion.getKind();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DocumentEdits getTextEdit() {
|
||||
DocumentEdits edits = new DocumentEdits(doc);
|
||||
edits.replace(startOffset + prefix.length(), endOffset, completion.getValue().substring(prefix.length()));
|
||||
return edits;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDetail() {
|
||||
return completion.getDetail();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Renderable getDocumentation() {
|
||||
return completion.getDocumentation();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.snippets;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.CompilationUnit;
|
||||
import org.eclipse.lsp4j.CompletionItemKind;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SnippetBuilder;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
|
||||
public class JavaSnippet {
|
||||
|
||||
private JavaSnippetContext context;
|
||||
|
||||
private String name;
|
||||
|
||||
private String template;
|
||||
|
||||
private List<String> imports;
|
||||
|
||||
private CompletionItemKind kind;
|
||||
|
||||
public JavaSnippet(String name, JavaSnippetContext context, CompletionItemKind kind, List<String> imports,
|
||||
String template) {
|
||||
super();
|
||||
this.context = context;
|
||||
this.name = name;
|
||||
this.template = template;
|
||||
this.imports = imports;
|
||||
this.kind = kind;
|
||||
}
|
||||
|
||||
public Optional<ICompletionProposal> generateCompletion(Supplier<SnippetBuilder> snippetBuilderFactory,
|
||||
DocumentRegion query, ASTNode node, CompilationUnit cu) {
|
||||
|
||||
if (context.appliesTo(node)) {
|
||||
return Optional.of(
|
||||
new JavaSnippetCompletion(snippetBuilderFactory,
|
||||
query,
|
||||
cu,
|
||||
this
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public String getTemplate() {
|
||||
return this.template;
|
||||
}
|
||||
|
||||
public Optional<List<String>> getImports() {
|
||||
return Optional.of(this.imports);
|
||||
}
|
||||
|
||||
public CompletionItemKind getKind() {
|
||||
return kind;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.snippets;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.IndentUtil;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SnippetBuilder;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
|
||||
/**
|
||||
* Respobsible for converting eclipse-like template string into lsp snippet text.
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
public class JavaSnippetBuilder{
|
||||
|
||||
private Supplier<SnippetBuilder> snippetBuilderFactory;
|
||||
|
||||
private static final Pattern PLACE_HOLDER = Pattern.compile("\\$\\{(.+?)\\}");
|
||||
|
||||
public JavaSnippetBuilder(Supplier<SnippetBuilder> snippetBuilderFactory) {
|
||||
this.snippetBuilderFactory = snippetBuilderFactory;
|
||||
}
|
||||
|
||||
public DocumentEdits createEdit(DocumentRegion query, String template) {
|
||||
IDocument doc = query.getDocument();
|
||||
IndentUtil indentUtil = new IndentUtil(doc);
|
||||
|
||||
DocumentEdits edit = new DocumentEdits(doc);
|
||||
String snippet = createSnippet(template);
|
||||
|
||||
String referenceIndent = indentUtil.getReferenceIndent(query.getStart(), doc);
|
||||
|
||||
if (!referenceIndent.contains("\t")) {
|
||||
snippet = indentUtil.covertTabsToSpace(snippet);
|
||||
}
|
||||
String indentedSnippet = indentUtil.applyIndentation(snippet, referenceIndent);
|
||||
|
||||
edit.replace(query.getStart(), query.getEnd(), indentedSnippet);
|
||||
return edit;
|
||||
}
|
||||
|
||||
private String createSnippet(String template) {
|
||||
Matcher matcher = PLACE_HOLDER.matcher(template);
|
||||
int start = 0;
|
||||
SnippetBuilder snippet = snippetBuilderFactory.get();
|
||||
while (matcher.find(start)) {
|
||||
int matchStart = matcher.start();
|
||||
snippet.text(template.substring(start, matchStart));
|
||||
int matchEnd = matcher.end();
|
||||
String placeHolderImage = template.substring(matcher.start(1), matcher.end(1));
|
||||
int colon = placeHolderImage.indexOf(':');
|
||||
String id, value;
|
||||
if (colon>=0) {
|
||||
id = placeHolderImage.substring(0, colon);
|
||||
value = placeHolderImage.substring(colon+1);
|
||||
} else {
|
||||
id = placeHolderImage;
|
||||
value = id;
|
||||
}
|
||||
snippet.placeHolder(id, value);
|
||||
start = matchEnd;
|
||||
}
|
||||
snippet.text(template.substring(start));
|
||||
return snippet.build().toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.snippets;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.jdt.core.dom.CompilationUnit;
|
||||
import org.eclipse.lsp4j.CompletionItemKind;
|
||||
import org.springframework.ide.vscode.boot.java.jdt.imports.ImportRewrite;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SnippetBuilder;
|
||||
import org.springframework.ide.vscode.commons.util.Renderable;
|
||||
import org.springframework.ide.vscode.commons.util.Renderables;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
|
||||
public class JavaSnippetCompletion implements ICompletionProposal{
|
||||
|
||||
private DocumentRegion query;
|
||||
private JavaSnippet javaSnippet;
|
||||
private Supplier<SnippetBuilder> snippetBuilderFactory;
|
||||
private CompilationUnit cu;
|
||||
|
||||
public JavaSnippetCompletion(Supplier<SnippetBuilder> snippetBuilderFactory, DocumentRegion query, CompilationUnit cu, JavaSnippet javaSnippet) {
|
||||
this.snippetBuilderFactory = snippetBuilderFactory;
|
||||
this.query = query;
|
||||
this.cu = cu;
|
||||
this.javaSnippet = javaSnippet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return javaSnippet.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletionItemKind getKind() {
|
||||
return javaSnippet.getKind();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DocumentEdits getTextEdit() {
|
||||
return new JavaSnippetBuilder(snippetBuilderFactory).createEdit(query, javaSnippet.getTemplate());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDetail() {
|
||||
return "Snippet";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Renderable getDocumentation() {
|
||||
return Renderables.NO_DESCRIPTION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<DocumentEdits> getAdditionalEdit() {
|
||||
ImportRewrite rewrite = ImportRewrite.create(cu, true);
|
||||
|
||||
javaSnippet.getImports().ifPresent((imprts ->
|
||||
{
|
||||
for (String imprt : imprts) {
|
||||
rewrite.addImport(imprt);
|
||||
}
|
||||
}));
|
||||
|
||||
DocumentEdits edit = rewrite.createEdit(query.getDocument());
|
||||
|
||||
return edit != null ? Optional.of(edit) : Optional.empty();
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.snippets;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.TypeDeclaration;
|
||||
|
||||
public interface JavaSnippetContext {
|
||||
JavaSnippetContext BOOT_MEMBERS = (node) -> node instanceof TypeDeclaration;
|
||||
|
||||
boolean appliesTo(ASTNode node);
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.snippets;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.CompilationUnit;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.PrefixFinder;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SnippetBuilder;
|
||||
import org.springframework.ide.vscode.commons.util.FuzzyMatcher;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
|
||||
public class JavaSnippetManager {
|
||||
|
||||
private List<JavaSnippet> snippets = new ArrayList<>();
|
||||
private Supplier<SnippetBuilder> snippetBuilderFactory;
|
||||
|
||||
private static PrefixFinder PREFIX_FINDER = new PrefixFinder() {
|
||||
|
||||
@Override
|
||||
protected boolean isPrefixChar(char c) {
|
||||
return Character.isJavaIdentifierPart(c);
|
||||
}
|
||||
};
|
||||
|
||||
public JavaSnippetManager(Supplier<SnippetBuilder> snippetBuilderFactory) {
|
||||
this.snippetBuilderFactory = snippetBuilderFactory;
|
||||
}
|
||||
|
||||
public void add(JavaSnippet javaSnippet) {
|
||||
snippets.add(javaSnippet);
|
||||
|
||||
}
|
||||
|
||||
public Collection<ICompletionProposal> getCompletions(IDocument doc, int offset, ASTNode node, CompilationUnit cu) {
|
||||
Collection<ICompletionProposal> completions = new ArrayList<>();
|
||||
|
||||
DocumentRegion query = PREFIX_FINDER.getPrefixRegion(doc, offset);
|
||||
|
||||
for (JavaSnippet javaSnippet : snippets) {
|
||||
if (FuzzyMatcher.matchScore(query.toString(), javaSnippet.getName()) != 0) {
|
||||
javaSnippet.generateCompletion(snippetBuilderFactory, query, node, cu)
|
||||
.ifPresent((completion) -> completions.add(completion));
|
||||
}
|
||||
}
|
||||
|
||||
return completions;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
<!-- These are the original templates. This file will be deleted. It just here for 'inspiration' -->
|
||||
<templates>
|
||||
<template autoinsert="true"
|
||||
id="org.springframework.ide.eclipse.boot.templates.RequestMapping"
|
||||
name="RequestMapping method" context="boot-members" description="RequestMapping method"
|
||||
enabled="true">${x:import(org.springframework.web.bind.annotation.RequestMapping,
|
||||
org.springframework.web.bind.annotation.RequestMethod,
|
||||
org.springframework.web.bind.annotation.RequestParam)}@RequestMapping(value="${path}",
|
||||
method=RequestMethod.${GET})
|
||||
public ${SomeData} ${requestMethodName}(@RequestParam ${String} ${param}) {
|
||||
return new ${SomeData}(${cursor});
|
||||
}
|
||||
</template>
|
||||
<template autoinsert="true"
|
||||
id="org.springframework.ide.eclipse.boot.templates.GetMapping" name="GetMapping method"
|
||||
context="boot-members" description="GetMapping method" enabled="true">
|
||||
${x:import(org.springframework.web.bind.annotation.GetMapping,
|
||||
org.springframework.web.bind.annotation.RequestParam)}@GetMapping(value="${path}")
|
||||
public ${SomeData} ${getMethodName}(@RequestParam ${String} ${param})
|
||||
{
|
||||
return new ${SomeData}(${cursor});
|
||||
}
|
||||
</template>
|
||||
<template autoinsert="true"
|
||||
id="org.springframework.ide.eclipse.boot.templates.PostMapping" name="PostMapping method"
|
||||
context="boot-members" description="PostMapping method" enabled="true">
|
||||
${x:import(org.springframework.web.bind.annotation.PostMapping,
|
||||
org.springframework.web.bind.annotation.RequestBody)}@PostMapping(value="${path}")
|
||||
public ${SomeEnityData} ${postMethodName}(@RequestBody
|
||||
${SomeEnityData} ${entity}) {
|
||||
//TODO: process POST request
|
||||
${cursor}
|
||||
return ${entity};
|
||||
}
|
||||
</template>
|
||||
<template autoinsert="true"
|
||||
id="org.springframework.ide.eclipse.boot.templates.PutMapping" name="PutMapping method"
|
||||
context="boot-members" description="PutMapping method" enabled="true">
|
||||
${x:import(org.springframework.web.bind.annotation.PutMapping,
|
||||
org.springframework.web.bind.annotation.RequestBody,
|
||||
org.springframework.web.bind.annotation.PathVariable)}@PutMapping(value="${path}/{${id}}")
|
||||
public ${SomeEnityData} ${putMethodName}(@PathVariable
|
||||
${pvt:link(String,int,long)} ${id}, @RequestBody ${SomeEnityData}
|
||||
${entity}) {
|
||||
//TODO: process PUT request
|
||||
${cursor}
|
||||
return ${entity};
|
||||
}
|
||||
</template>
|
||||
</templates>
|
||||
@@ -1,245 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2018 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.ArrayInitializer;
|
||||
import org.eclipse.jdt.core.dom.Expression;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.MemberValuePair;
|
||||
import org.eclipse.jdt.core.dom.MethodDeclaration;
|
||||
import org.eclipse.jdt.core.dom.NormalAnnotation;
|
||||
import org.eclipse.jdt.core.dom.QualifiedName;
|
||||
import org.eclipse.jdt.core.dom.SimpleName;
|
||||
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
|
||||
import org.eclipse.jdt.core.dom.StringLiteral;
|
||||
import org.eclipse.jdt.core.dom.TypeDeclaration;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion;
|
||||
import org.springframework.ide.vscode.commons.util.CollectorUtil;
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
public class ASTUtils {
|
||||
|
||||
public static DocumentRegion nameRegion(TextDocument doc, Annotation annotation) {
|
||||
int start = annotation.getTypeName().getStartPosition();
|
||||
int end = start + annotation.getTypeName().getLength();
|
||||
if (doc.getSafeChar(start - 1) == '@') {
|
||||
start--;
|
||||
}
|
||||
return new DocumentRegion(doc, start, end);
|
||||
}
|
||||
|
||||
public static Optional<Range> nameRange(TextDocument doc, Annotation annotation) {
|
||||
try {
|
||||
return Optional.of(nameRegion(doc, annotation).asRange());
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
public static DocumentRegion stringRegion(TextDocument doc, StringLiteral node) {
|
||||
DocumentRegion nodeRegion = nodeRegion(doc, node);
|
||||
if (nodeRegion.startsWith("\"")) {
|
||||
nodeRegion = nodeRegion.subSequence(1);
|
||||
}
|
||||
if (nodeRegion.endsWith("\"")) {
|
||||
nodeRegion = nodeRegion.subSequence(0, nodeRegion.getLength()-1);
|
||||
}
|
||||
return nodeRegion;
|
||||
}
|
||||
|
||||
|
||||
public static DocumentRegion nodeRegion(TextDocument doc, ASTNode node) {
|
||||
int start = node.getStartPosition();
|
||||
int end = start + node.getLength();
|
||||
return new DocumentRegion(doc, start, end);
|
||||
}
|
||||
|
||||
public static Optional<Expression> getAttribute(Annotation annotation, String name) {
|
||||
if (annotation != null) {
|
||||
try {
|
||||
if (annotation.isSingleMemberAnnotation() && name.equals("value")) {
|
||||
SingleMemberAnnotation sma = (SingleMemberAnnotation) annotation;
|
||||
return Optional.ofNullable(sma.getValue());
|
||||
} else if (annotation.isNormalAnnotation()) {
|
||||
NormalAnnotation na = (NormalAnnotation) annotation;
|
||||
Object attributeObjs = na.getStructuralProperty(NormalAnnotation.VALUES_PROPERTY);
|
||||
if (attributeObjs instanceof List) {
|
||||
for (Object atrObj : (List<?>)attributeObjs) {
|
||||
if (atrObj instanceof MemberValuePair) {
|
||||
MemberValuePair mvPair = (MemberValuePair) atrObj;
|
||||
if (name.equals(mvPair.getName().getIdentifier())) {
|
||||
return Optional.ofNullable(mvPair.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* For case where a expression can be either a String or a array of Strings and
|
||||
* we are interested in the first element of the array. (I.e. typical case
|
||||
* when annotation attribute is of type String[] (because Java allows using a single
|
||||
* value as a convenient syntax for writing an array of length 1 in that case.
|
||||
*/
|
||||
public static Optional<String> getFirstString(Expression exp) {
|
||||
if (exp instanceof StringLiteral) {
|
||||
return Optional.ofNullable(getLiteralValue((StringLiteral) exp));
|
||||
} else if (exp instanceof ArrayInitializer) {
|
||||
ArrayInitializer array = (ArrayInitializer) exp;
|
||||
Object objs = array.getStructuralProperty(ArrayInitializer.EXPRESSIONS_PROPERTY);
|
||||
if (objs instanceof List) {
|
||||
List<?> list = (List<?>) objs;
|
||||
if (!list.isEmpty()) {
|
||||
Object firstObj = list.get(0);
|
||||
if (firstObj instanceof Expression) {
|
||||
return getFirstString((Expression) firstObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public static TypeDeclaration findDeclaringType(Annotation annotation) {
|
||||
ASTNode node = annotation;
|
||||
while (node != null && !(node instanceof TypeDeclaration)) {
|
||||
node = node.getParent();
|
||||
}
|
||||
|
||||
return node != null ? (TypeDeclaration) node : null;
|
||||
}
|
||||
|
||||
public static MethodDeclaration[] findConstructors(TypeDeclaration typeDecl) {
|
||||
List<MethodDeclaration> constructors = new ArrayList<>();
|
||||
|
||||
MethodDeclaration[] methods = typeDecl.getMethods();
|
||||
for (MethodDeclaration methodDeclaration : methods) {
|
||||
if (methodDeclaration.isConstructor()) {
|
||||
constructors.add(methodDeclaration);
|
||||
}
|
||||
}
|
||||
|
||||
return constructors.toArray(new MethodDeclaration[constructors.size()]);
|
||||
}
|
||||
|
||||
|
||||
public static MethodDeclaration getAnnotatedMethod(Annotation annotation) {
|
||||
ASTNode parent = annotation.getParent();
|
||||
if (parent instanceof MethodDeclaration) {
|
||||
return (MethodDeclaration)parent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static TypeDeclaration getAnnotatedType(Annotation annotation) {
|
||||
ASTNode parent = annotation.getParent();
|
||||
if (parent instanceof TypeDeclaration) {
|
||||
return (TypeDeclaration)parent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getLiteralValue(StringLiteral node) {
|
||||
synchronized (node.getAST()) {
|
||||
return node.getLiteralValue();
|
||||
}
|
||||
}
|
||||
|
||||
public static String getExpressionValueAsString(Expression exp) {
|
||||
if (exp instanceof StringLiteral) {
|
||||
return getLiteralValue((StringLiteral) exp);
|
||||
} else if (exp instanceof QualifiedName) {
|
||||
return getExpressionValueAsString(((QualifiedName) exp).getName());
|
||||
} else if (exp instanceof SimpleName) {
|
||||
return ((SimpleName) exp).getIdentifier();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static String[] getExpressionValueAsArray(Expression exp) {
|
||||
if (exp instanceof ArrayInitializer) {
|
||||
ArrayInitializer array = (ArrayInitializer) exp;
|
||||
return ((List<Expression>) array.expressions()).stream().map(e -> getExpressionValueAsString(e))
|
||||
.filter(Objects::nonNull).toArray(String[]::new);
|
||||
} else {
|
||||
String rm = getExpressionValueAsString(exp);
|
||||
if (rm != null) {
|
||||
return new String[] { rm };
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static List<StringLiteral> getExpressionValueAsListOfLiterals(Expression exp) {
|
||||
if (exp instanceof ArrayInitializer) {
|
||||
ArrayInitializer array = (ArrayInitializer) exp;
|
||||
return ((List<Expression>) array.expressions()).stream()
|
||||
.flatMap(e -> e instanceof StringLiteral
|
||||
? Stream.of((StringLiteral)e)
|
||||
: Stream.empty()
|
||||
)
|
||||
.collect(CollectorUtil.toImmutableList());
|
||||
} else if (exp instanceof StringLiteral){
|
||||
return ImmutableList.of((StringLiteral)exp);
|
||||
}
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
|
||||
public static Collection<Annotation> getAnnotations(TypeDeclaration declaringType) {
|
||||
Object modifiersObj = declaringType.getStructuralProperty(TypeDeclaration.MODIFIERS2_PROPERTY);
|
||||
if (modifiersObj instanceof List) {
|
||||
ImmutableList.Builder<Annotation> annotations = ImmutableList.builder();
|
||||
for (Object node : (List<?>)modifiersObj) {
|
||||
if (node instanceof Annotation) {
|
||||
annotations.add((Annotation) node);
|
||||
}
|
||||
}
|
||||
return annotations.build();
|
||||
}
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
|
||||
public static String getAnnotationType(Annotation annotation) {
|
||||
ITypeBinding binding = annotation.resolveTypeBinding();
|
||||
if (binding!=null) {
|
||||
return binding.getQualifiedName();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,201 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2018 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.utils;
|
||||
|
||||
import java.net.URI;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.core.JavaCore;
|
||||
import org.eclipse.jdt.core.dom.AST;
|
||||
import org.eclipse.jdt.core.dom.ASTParser;
|
||||
import org.eclipse.jdt.core.dom.CompilationUnit;
|
||||
import org.springframework.ide.vscode.commons.java.IClasspath;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService;
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
|
||||
public final class CompilationUnitCache {
|
||||
|
||||
private JavaProjectFinder projectFinder;
|
||||
private ProjectObserver projectObserver;
|
||||
private Cache<URI, CompilationUnit> uriToCu;
|
||||
private Cache<IJavaProject, Set<URI>> projectToDocs;
|
||||
private ProjectObserver.Listener projectListener;
|
||||
|
||||
private ReadLock readLock;
|
||||
private WriteLock writeLock;
|
||||
|
||||
public CompilationUnitCache(JavaProjectFinder projectFinder, SimpleTextDocumentService documentService, ProjectObserver projectObserver) {
|
||||
this.projectFinder = projectFinder;
|
||||
this.projectObserver = projectObserver;
|
||||
projectListener = new CUProjectListener();
|
||||
|
||||
uriToCu = CacheBuilder.newBuilder().build();
|
||||
projectToDocs = CacheBuilder.newBuilder().build();
|
||||
|
||||
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
|
||||
readLock = lock.readLock();
|
||||
writeLock = lock.writeLock();
|
||||
|
||||
if (documentService != null) {
|
||||
documentService.onDidChangeContent(doc -> invalidateCuForJavaFile(doc.getDocument().getId().getUri()));
|
||||
documentService.onDidClose(doc -> invalidateCuForJavaFile(doc.getId().getUri()));
|
||||
}
|
||||
|
||||
if (this.projectObserver != null) {
|
||||
this.projectObserver.addListener(projectListener);
|
||||
}
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
if (projectObserver != null) {
|
||||
projectObserver.removeListener(projectListener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a CompiationUnitn AST from the cache and passes it to a requestor callback, applying
|
||||
* proper thread synchronization around the requestor.
|
||||
* <p>
|
||||
* Warning: Callers should take care to do all AST processing inside of the requestor callback and
|
||||
* not pass of AST nodes to helper functions that work aynchronously or store AST nodes or ITypeBindings
|
||||
* for later use. The JDT ASTs are not thread safe!
|
||||
*/
|
||||
public <T> T withCompilationUnit(TextDocument document, Function<CompilationUnit, T> requestor) {
|
||||
URI uri = URI.create(document.getUri());
|
||||
IJavaProject project = projectFinder.find(document.getId()).orElse(null);
|
||||
|
||||
if (project != null) {
|
||||
|
||||
readLock.lock();
|
||||
CompilationUnit cu = null;
|
||||
|
||||
try {
|
||||
cu = uriToCu.get(uri, () -> {
|
||||
CompilationUnit cUnit = parse(document, project);
|
||||
projectToDocs.get(project, () -> new HashSet<>()).add(URI.create(document.getUri()));
|
||||
return cUnit;
|
||||
});
|
||||
if (cu != null) {
|
||||
projectToDocs.get(project, () -> new HashSet<>()).add(URI.create(document.getUri()));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
|
||||
if (cu != null) {
|
||||
try {
|
||||
synchronized (cu.getAST()) {
|
||||
return requestor.apply(cu);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
Log.log(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return requestor.apply(null);
|
||||
}
|
||||
|
||||
private void invalidateCuForJavaFile(String uriStr) {
|
||||
URI uri = URI.create(uriStr);
|
||||
writeLock.lock();
|
||||
try {
|
||||
uriToCu.invalidate(uri);
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public static CompilationUnit parse(TextDocument document, IJavaProject project) throws Exception {
|
||||
ASTParser parser = ASTParser.newParser(AST.JLS9);
|
||||
Map<String, String> options = JavaCore.getOptions();
|
||||
JavaCore.setComplianceOptions(JavaCore.VERSION_1_8, options);
|
||||
parser.setCompilerOptions(options);
|
||||
parser.setKind(ASTParser.K_COMPILATION_UNIT);
|
||||
parser.setStatementsRecovery(true);
|
||||
parser.setBindingsRecovery(true);
|
||||
parser.setResolveBindings(true);
|
||||
|
||||
String[] classpathEntries = getClasspathEntries(document, project);
|
||||
String[] sourceEntries = new String[] {};
|
||||
parser.setEnvironment(classpathEntries, sourceEntries, null, true);
|
||||
|
||||
String docURI = document.getUri();
|
||||
String unitName = docURI.substring(docURI.lastIndexOf("/"));
|
||||
parser.setUnitName(unitName);
|
||||
parser.setSource(document.get(0, document.getLength()).toCharArray());
|
||||
|
||||
CompilationUnit cu = (CompilationUnit) parser.createAST(null);
|
||||
|
||||
return cu;
|
||||
}
|
||||
|
||||
private static String[] getClasspathEntries(TextDocument document, IJavaProject project) throws Exception {
|
||||
if (project == null) {
|
||||
return new String[0];
|
||||
} else {
|
||||
IClasspath classpath = project.getClasspath();
|
||||
Stream<Path> classpathEntries = classpath.getClasspathEntries().stream();
|
||||
return classpathEntries
|
||||
.filter(path -> path.toFile().exists())
|
||||
.map(path -> path.toAbsolutePath().toString()).toArray(String[]::new);
|
||||
}
|
||||
}
|
||||
|
||||
private void invalidateProject(IJavaProject project) {
|
||||
Set<URI> docUris = projectToDocs.getIfPresent(project);
|
||||
if (docUris != null) {
|
||||
writeLock.lock();
|
||||
try {
|
||||
uriToCu.invalidateAll(docUris);
|
||||
projectToDocs.invalidate(project);
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class CUProjectListener implements ProjectObserver.Listener {
|
||||
|
||||
@Override
|
||||
public void created(IJavaProject project) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changed(IJavaProject project) {
|
||||
invalidateProject(project);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleted(IJavaProject project) {
|
||||
invalidateProject(project);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2018 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.utils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.Modifier;
|
||||
import org.eclipse.jdt.core.dom.TypeDeclaration;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
import reactor.util.function.Tuple3;
|
||||
import reactor.util.function.Tuples;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class FunctionUtils {
|
||||
|
||||
public static final String FUNCTION_FUNCTION_TYPE = Function.class.getName();
|
||||
public static final String FUNCTION_CONSUMER_TYPE = Consumer.class.getName();
|
||||
public static final String FUNCTION_SUPPLIER_TYPE = Supplier.class.getName();
|
||||
|
||||
public static Tuple3<String, String, DocumentRegion> getFunctionBean(TypeDeclaration typeDeclaration, TextDocument doc) {
|
||||
ITypeBinding resolvedType = typeDeclaration.resolveBinding();
|
||||
|
||||
if (resolvedType != null && !resolvedType.isInterface() && !isAbstractClass(typeDeclaration, resolvedType)) {
|
||||
return getFunctionBean(typeDeclaration, doc, resolvedType);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static Tuple3<String, String, DocumentRegion> getFunctionBean(TypeDeclaration typeDeclaration, TextDocument doc,
|
||||
ITypeBinding resolvedType) {
|
||||
|
||||
ITypeBinding[] interfaces = resolvedType.getInterfaces();
|
||||
for (ITypeBinding resolvedInterface : interfaces) {
|
||||
String simplifiedType = null;
|
||||
if (resolvedInterface.isParameterizedType()) {
|
||||
simplifiedType = resolvedInterface.getBinaryName();
|
||||
}
|
||||
else {
|
||||
simplifiedType = resolvedType.getQualifiedName();
|
||||
}
|
||||
|
||||
if (FUNCTION_FUNCTION_TYPE.equals(simplifiedType) || FUNCTION_CONSUMER_TYPE.equals(simplifiedType)
|
||||
|| FUNCTION_SUPPLIER_TYPE.equals(simplifiedType)) {
|
||||
String beanName = getBeanName(typeDeclaration);
|
||||
String beanType = resolvedInterface.getName();
|
||||
DocumentRegion region = ASTUtils.nodeRegion(doc, typeDeclaration.getName());
|
||||
|
||||
return Tuples.of(beanName, beanType, region);
|
||||
}
|
||||
else {
|
||||
Tuple3<String, String, DocumentRegion> result = getFunctionBean(typeDeclaration, doc, resolvedInterface);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ITypeBinding superclass = resolvedType.getSuperclass();
|
||||
if (superclass != null) {
|
||||
return getFunctionBean(typeDeclaration, doc, superclass);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected static String getBeanName(TypeDeclaration typeDeclaration) {
|
||||
String beanName = typeDeclaration.getName().toString();
|
||||
if (beanName.length() > 0 && Character.isUpperCase(beanName.charAt(0))) {
|
||||
beanName = Character.toLowerCase(beanName.charAt(0)) + beanName.substring(1);
|
||||
}
|
||||
return beanName;
|
||||
}
|
||||
|
||||
protected static boolean isAbstractClass(TypeDeclaration typeDeclaration, ITypeBinding resolvedType) {
|
||||
List<?> modifiers = typeDeclaration.modifiers();
|
||||
for (Object object : modifiers) {
|
||||
if (object instanceof Modifier) {
|
||||
if (((Modifier) object).isAbstract()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2018 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.ide.vscode.commons.java.IClasspath;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.LspClient;
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
|
||||
/**
|
||||
* Utility for creating code navigate links for Markdown format documentation.
|
||||
* The utility looks at the environment variable "sts.lsp.client" to create a URL supported on a specific client
|
||||
*
|
||||
* @author Alex Boyko
|
||||
*
|
||||
*/
|
||||
public class SourceLinks {
|
||||
|
||||
static final String JAR = ".jar";
|
||||
static final String JAVA = ".java";
|
||||
static final String CLASS = ".class";
|
||||
|
||||
public static Optional<String> sourceLinkUrlForFQName(IJavaProject project, String fqName) {
|
||||
switch (LspClient.currentClient()) {
|
||||
case VSCODE:
|
||||
return createVSCodeSourceLink(project, fqName);
|
||||
default:
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
public static Optional<String> sourceLinkUrlForClasspathResource(IJavaProject project, String path) {
|
||||
switch (LspClient.currentClient()) {
|
||||
case VSCODE:
|
||||
return createVSCodeSourceLinkForClasspathResource(project, path);
|
||||
default:
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
public static Optional<String> sourceLinkForResourcePath(Path path) {
|
||||
switch (LspClient.currentClient()) {
|
||||
case VSCODE:
|
||||
return Optional.of(createVSCodeLink(path));
|
||||
default:
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
private static Optional<String> createVSCodeSourceLinkForClasspathResource(IJavaProject project, String path) {
|
||||
int idx = path.lastIndexOf(SourceLinks.CLASS);
|
||||
if (idx >= 0) {
|
||||
Path p = Paths.get(path.substring(0, idx));
|
||||
return SourceLinks.sourceLinkUrlForFQName(project, p.toString().replace(File.separator, "."));
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private static String createVSCodeLink(Path path) {
|
||||
return path.toUri().toString();
|
||||
}
|
||||
|
||||
private static Optional<String> createVSCodeSourceLink(IJavaProject project, String fqName) {
|
||||
Optional<File> classpathResource = project.getClasspath().findClasspathResourceContainer(fqName);
|
||||
if (classpathResource.isPresent()) {
|
||||
File file = classpathResource.get();
|
||||
if (file.isDirectory()) {
|
||||
return javaSourceLinkUrl(project, fqName, file);
|
||||
} else if (file.getName().endsWith(JAR)) {
|
||||
return jarSourceLinkUrl(project, fqName, file);
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private static Optional<String> javaSourceLinkUrl(IJavaProject project, String javaRelativePath, String fqName) {
|
||||
Path path = Paths.get(javaRelativePath);
|
||||
Optional<Path> sourceResource = project.getClasspath().getSourceFolders().stream()
|
||||
.map(r -> Paths.get(r).resolve(path)).filter(r -> Files.exists(r)).findFirst();
|
||||
if (sourceResource.isPresent()) {
|
||||
return Optional.of(sourceResource.get().toUri().toString());
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private static Optional<String> javaSourceLinkUrl(IJavaProject project, String fqName, File containerFolder) {
|
||||
IClasspath classpath = project.getClasspath();
|
||||
if (containerFolder.toPath().startsWith(classpath.getOutputFolder())) {
|
||||
int innerTypeIdx = fqName.indexOf('$');
|
||||
String topLevelType = innerTypeIdx > 0 ? fqName.substring(0, innerTypeIdx) : fqName;
|
||||
return javaSourceLinkUrl(project, topLevelType.replaceAll("\\.", "/") + JAVA, fqName);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private static Optional<String> jarSourceLinkUrl(IJavaProject project, String fqName, File jarFile) {
|
||||
try {
|
||||
int lastDotIndex = fqName.lastIndexOf('.');
|
||||
String packageName = fqName.substring(0, lastDotIndex);
|
||||
String typeName = fqName.substring(lastDotIndex + 1);
|
||||
String jarFileName = jarFile.getName();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("jdt://contents/");
|
||||
sb.append(jarFileName);
|
||||
sb.append("/");
|
||||
sb.append(packageName);
|
||||
sb.append("/");
|
||||
sb.append(typeName);
|
||||
sb.append(CLASS);
|
||||
sb.append("?");
|
||||
|
||||
StringBuilder query = new StringBuilder();
|
||||
query.append("=");
|
||||
query.append(project.getElementName());
|
||||
query.append("/");
|
||||
String convertedPath = jarFile.toString().replace(File.separator, "\\/");
|
||||
query.append(convertedPath);
|
||||
query.append("<");
|
||||
query.append(packageName);
|
||||
query.append("(");
|
||||
query.append(typeName);
|
||||
query.append(CLASS);
|
||||
|
||||
sb.append(URLEncoder.encode(query.toString(), "UTF8"));
|
||||
|
||||
return Optional.of(sb.toString());
|
||||
} catch (Throwable t) {
|
||||
Log.log(t);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
// private static Optional<Region> findTypeRange(CompilationUnit cu, String fqName) {
|
||||
// int[] values = new int[] {0, -1};
|
||||
// int lastDotIndex = fqName.lastIndexOf('.');
|
||||
// String packageName = fqName.substring(0, lastDotIndex);
|
||||
// String typeName = fqName.substring(lastDotIndex + 1);
|
||||
// if (packageName.equals(cu.getPackage().getName().getFullyQualifiedName())) {
|
||||
// Stack<String> visitedType = new Stack<>();
|
||||
// cu.accept(new ASTVisitor() {
|
||||
//
|
||||
// @Override
|
||||
// public boolean visit(TypeDeclaration node) {
|
||||
// if (values[1] < 0) {
|
||||
// visitedType.push(node.getName().getIdentifier());
|
||||
// if (String.join("$", visitedType.toArray(new String[visitedType.size()])).equals(typeName)) {
|
||||
// values[0] = node.getName().getStartPosition();
|
||||
// values[1] = node.getName().getLength();
|
||||
// }
|
||||
// }
|
||||
// return values[1] < 0;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void endVisit(TypeDeclaration node) {
|
||||
// visitedType.pop();
|
||||
// super.endVisit(node);
|
||||
// }
|
||||
//
|
||||
// });
|
||||
// }
|
||||
// return Optional.ofNullable(values[1] < 0 ? null : new Region(values[0], values[1]));
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -1,632 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2018 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.eclipse.jdt.core.JavaCore;
|
||||
import org.eclipse.jdt.core.dom.AST;
|
||||
import org.eclipse.jdt.core.dom.ASTParser;
|
||||
import org.eclipse.jdt.core.dom.ASTVisitor;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.CompilationUnit;
|
||||
import org.eclipse.jdt.core.dom.FileASTRequestor;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.MarkerAnnotation;
|
||||
import org.eclipse.jdt.core.dom.NormalAnnotation;
|
||||
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
|
||||
import org.eclipse.jdt.core.dom.TypeDeclaration;
|
||||
import org.eclipse.lsp4j.Location;
|
||||
import org.eclipse.lsp4j.SymbolInformation;
|
||||
import org.eclipse.lsp4j.SymbolKind;
|
||||
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ide.vscode.boot.java.BootJavaLanguageServer;
|
||||
import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchies;
|
||||
import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchyAwareLookup;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider;
|
||||
import org.springframework.ide.vscode.commons.java.IClasspath;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver.Listener;
|
||||
import org.springframework.ide.vscode.commons.languageserver.multiroot.WorkspaceFolder;
|
||||
import org.springframework.ide.vscode.commons.util.StringUtil;
|
||||
import org.springframework.ide.vscode.commons.util.UriUtil;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class SpringIndexer {
|
||||
|
||||
private final BootJavaLanguageServer server;
|
||||
private final JavaProjectFinder projectFinder;
|
||||
private final AnnotationHierarchyAwareLookup<SymbolProvider> symbolProviders;
|
||||
|
||||
private final List<SymbolInformation> symbols;
|
||||
private final ConcurrentMap<String, List<SymbolInformation>> symbolsByDoc;
|
||||
|
||||
private final Thread updateWorker;
|
||||
private final BlockingQueue<WorkerItem> updateQueue;
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SpringIndexer.class);
|
||||
|
||||
private final Listener projectListener = new Listener() {
|
||||
|
||||
@Override
|
||||
public void created(IJavaProject project) {
|
||||
log.debug("project created event: {}", project.getElementName());
|
||||
refresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changed(IJavaProject project) {
|
||||
log.debug("project changed event: {}", project.getElementName());
|
||||
refresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleted(IJavaProject project) {
|
||||
log.debug("project deleted event: {}", project.getElementName());
|
||||
refresh();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
private volatile InitializeItem lastInitializeItem;
|
||||
|
||||
public SpringIndexer(BootJavaLanguageServer server, JavaProjectFinder projectFinder, AnnotationHierarchyAwareLookup<SymbolProvider> specificProviders) {
|
||||
this.server = server;
|
||||
this.projectFinder = projectFinder;
|
||||
this.symbolProviders = specificProviders;
|
||||
|
||||
this.symbols = Collections.synchronizedList(new ArrayList<>());
|
||||
this.symbolsByDoc = new ConcurrentHashMap<>();
|
||||
|
||||
this.updateQueue = new LinkedBlockingQueue<>();
|
||||
this.updateWorker = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
while (true) {
|
||||
WorkerItem workerItem = updateQueue.take();
|
||||
workerItem.run();
|
||||
}
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
// ignore
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}, "Spring Annotation Index Update Worker");
|
||||
updateWorker.start();
|
||||
|
||||
server.getWorkspaceService().onDidChangeWorkspaceFolders(evt -> {
|
||||
log.debug("workspace roots have changed event arrived - added: " + Arrays.toString(evt.getEvent().getAdded()) + " - removed: " + Arrays.toString(evt.getEvent().getRemoved()));
|
||||
refresh();
|
||||
});
|
||||
|
||||
if (server.getProjectObserver() != null) {
|
||||
server.getProjectObserver().addListener(projectListener);
|
||||
}
|
||||
}
|
||||
|
||||
public void serverInitialized() {
|
||||
List<String> globPattern = Arrays.asList("**/*.java");
|
||||
server.getWorkspaceService().getFileObserver().onFileDeleted(globPattern, (file) -> {
|
||||
deleteDocument(new TextDocumentIdentifier(file).getUri());
|
||||
});
|
||||
server.getWorkspaceService().getFileObserver().onFileCreated(globPattern, (file) -> {
|
||||
createDocument(new TextDocumentIdentifier(file).getUri());
|
||||
});
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> initialize(Collection<WorkspaceFolder> workspaceRoots) {
|
||||
synchronized(this) {
|
||||
try {
|
||||
if (lastInitializeItem != null && !lastInitializeItem.getFuture().isDone()) {
|
||||
lastInitializeItem.getFuture().cancel(false);
|
||||
}
|
||||
|
||||
lastInitializeItem = new InitializeItem(workspaceRoots.toArray(new WorkspaceFolder[workspaceRoots.size()]));
|
||||
updateQueue.put(lastInitializeItem);
|
||||
return lastInitializeItem.getFuture();
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("{}", e);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isInitializing() {
|
||||
return lastInitializeItem != null && !lastInitializeItem.getFuture().isDone();
|
||||
}
|
||||
|
||||
public void waitForInitializeTask() {
|
||||
synchronized (this) {
|
||||
if (lastInitializeItem != null) {
|
||||
try {
|
||||
lastInitializeItem.getFuture().get();
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void refresh() {
|
||||
synchronized (this) {
|
||||
symbols.clear();
|
||||
symbolsByDoc.clear();
|
||||
|
||||
Collection<WorkspaceFolder> roots = server.getWorkspaceRoots();
|
||||
log.debug("refresh spring indexer for roots: {}", roots.toString());
|
||||
initialize(roots);
|
||||
}
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
try {
|
||||
synchronized(this) {
|
||||
if (updateWorker != null && updateWorker.isAlive()) {
|
||||
updateWorker.interrupt();
|
||||
}
|
||||
|
||||
if (server.getProjectObserver() != null) {
|
||||
server.getProjectObserver().removeListener(projectListener);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("{}", e);
|
||||
}
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> updateDocument(String docURI, String content) {
|
||||
synchronized(this) {
|
||||
if (docURI.endsWith(".java") && lastInitializeItem != null) {
|
||||
try {
|
||||
Optional<IJavaProject> maybeProject = projectFinder.find(new TextDocumentIdentifier(docURI));
|
||||
if (maybeProject.isPresent()) {
|
||||
String[] classpathEntries = getClasspathEntries(maybeProject.get());
|
||||
|
||||
UpdateItem updateItem = new UpdateItem(docURI, content, classpathEntries);
|
||||
updateQueue.put(updateItem);
|
||||
return updateItem.getFuture();
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("{}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> deleteDocument(String deletedDocURI) {
|
||||
synchronized(this) {
|
||||
try {
|
||||
DeleteItem deleteItem = new DeleteItem(deletedDocURI);
|
||||
updateQueue.put(deleteItem);
|
||||
return deleteItem.getFuture();
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("{}", e);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> createDocument(String docURI) {
|
||||
synchronized(this) {
|
||||
if (docURI.endsWith(".java") && lastInitializeItem != null) {
|
||||
try {
|
||||
Optional<IJavaProject> maybeProject = projectFinder.find(new TextDocumentIdentifier(docURI));
|
||||
if (maybeProject.isPresent()) {
|
||||
String[] classpathEntries = getClasspathEntries(maybeProject.get());
|
||||
|
||||
String content = FileUtils.readFileToString(new File(new URI(docURI)));
|
||||
UpdateItem updateItem = new UpdateItem(docURI, content, classpathEntries);
|
||||
updateQueue.put(updateItem);
|
||||
return updateItem.getFuture();
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("{}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<SymbolInformation> getAllSymbols(String query) {
|
||||
waitForInitializeTask();
|
||||
|
||||
if (query != null && query.length() > 0) {
|
||||
return searchMatchingSymbols(this.symbols, query);
|
||||
} else {
|
||||
return this.symbols;
|
||||
}
|
||||
}
|
||||
|
||||
public List<? extends SymbolInformation> getSymbols(String docURI) {
|
||||
waitForInitializeTask();
|
||||
return this.symbolsByDoc.get(docURI);
|
||||
}
|
||||
|
||||
private List<SymbolInformation> searchMatchingSymbols(List<SymbolInformation> allsymbols, String query) {
|
||||
waitForInitializeTask();
|
||||
return allsymbols.stream()
|
||||
.filter(symbol -> StringUtil.containsCharactersCaseInsensitive(symbol.getName(), query))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private void scanFiles(WorkspaceFolder directory) {
|
||||
try {
|
||||
Map<Optional<IJavaProject>, List<String>> projects = Files.walk(Paths.get(new URI(directory.getUri())))
|
||||
.filter(path -> path.getFileName().toString().endsWith(".java"))
|
||||
.filter(Files::isRegularFile)
|
||||
.map(path -> path.toAbsolutePath().toString())
|
||||
.collect(Collectors.groupingBy((javaFile) -> projectFinder.find(new TextDocumentIdentifier(new File(javaFile).toURI().toString()))));
|
||||
|
||||
projects.forEach((maybeProject, files) -> maybeProject.ifPresent(project -> scanProject(project, files.toArray(new String[0]))));
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void scanProject(IJavaProject project, String[] files) {
|
||||
try {
|
||||
ASTParser parser = ASTParser.newParser(AST.JLS9);
|
||||
String[] classpathEntries = getClasspathEntries(project);
|
||||
|
||||
scanFiles(parser, files, classpathEntries);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void scanFile(String docURI, String content, String[] classpathEntries) throws Exception {
|
||||
ASTParser parser = ASTParser.newParser(AST.JLS9);
|
||||
Map<String, String> options = JavaCore.getOptions();
|
||||
JavaCore.setComplianceOptions(JavaCore.VERSION_1_8, options);
|
||||
parser.setCompilerOptions(options);
|
||||
parser.setKind(ASTParser.K_COMPILATION_UNIT);
|
||||
parser.setStatementsRecovery(true);
|
||||
parser.setBindingsRecovery(true);
|
||||
parser.setResolveBindings(true);
|
||||
|
||||
String[] sourceEntries = new String[] {};
|
||||
parser.setEnvironment(classpathEntries, sourceEntries, null, true);
|
||||
|
||||
String unitName = docURI.substring(docURI.lastIndexOf("/"));
|
||||
parser.setUnitName(unitName);
|
||||
parser.setSource(content.toCharArray());
|
||||
|
||||
CompilationUnit cu = (CompilationUnit) parser.createAST(null);
|
||||
|
||||
if (cu != null) {
|
||||
List<SymbolInformation> oldSymbols = symbolsByDoc.remove(docURI);
|
||||
if (oldSymbols != null) {
|
||||
symbols.removeAll(oldSymbols);
|
||||
}
|
||||
|
||||
AtomicReference<TextDocument> docRef = new AtomicReference<>();
|
||||
scanAST(cu, docURI, docRef, content);
|
||||
}
|
||||
}
|
||||
|
||||
private void scanFiles(ASTParser parser, String[] javaFiles, String[] classpathEntries) throws Exception {
|
||||
|
||||
Map<String, String> options = JavaCore.getOptions();
|
||||
JavaCore.setComplianceOptions(JavaCore.VERSION_1_8, options);
|
||||
parser.setCompilerOptions(options);
|
||||
parser.setKind(ASTParser.K_COMPILATION_UNIT);
|
||||
parser.setStatementsRecovery(true);
|
||||
parser.setBindingsRecovery(true);
|
||||
parser.setResolveBindings(true);
|
||||
parser.setIgnoreMethodBodies(true);
|
||||
|
||||
String[] sourceEntries = new String[] {};
|
||||
parser.setEnvironment(classpathEntries, sourceEntries, null, true);
|
||||
|
||||
FileASTRequestor requestor = new FileASTRequestor() {
|
||||
@Override
|
||||
public void acceptAST(String sourceFilePath, CompilationUnit cu) {
|
||||
String docURI = UriUtil.toUri(new File(sourceFilePath)).toString();
|
||||
AtomicReference<TextDocument> docRef = new AtomicReference<>();
|
||||
scanAST(cu, docURI, docRef, null);
|
||||
}
|
||||
};
|
||||
|
||||
parser.createASTs(javaFiles, null, new String[0], requestor, null);
|
||||
}
|
||||
|
||||
private void scanAST(final CompilationUnit cu, final String docURI, AtomicReference<TextDocument> docRef, final String content) {
|
||||
cu.accept(new ASTVisitor() {
|
||||
|
||||
@Override
|
||||
public boolean visit(TypeDeclaration node) {
|
||||
try {
|
||||
extractSymbolInformation(node, docURI, docRef, content);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return super.visit(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(SingleMemberAnnotation node) {
|
||||
try {
|
||||
extractSymbolInformation(node, docURI, docRef, content);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return super.visit(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(NormalAnnotation node) {
|
||||
try {
|
||||
extractSymbolInformation(node, docURI, docRef, content);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return super.visit(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(MarkerAnnotation node) {
|
||||
try {
|
||||
extractSymbolInformation(node, docURI, docRef, content);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return super.visit(node);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void extractSymbolInformation(TypeDeclaration typeDeclaration, String docURI, AtomicReference<TextDocument> docRef, String content) throws Exception {
|
||||
Collection<SymbolProvider> providers = symbolProviders.getAll();
|
||||
if (!providers.isEmpty()) {
|
||||
TextDocument doc = getTempTextDocument(docURI, docRef, content);
|
||||
for (SymbolProvider provider : providers) {
|
||||
Collection<SymbolInformation> sbls = provider.getSymbols(typeDeclaration, doc);
|
||||
if (sbls != null) {
|
||||
sbls.forEach(symbol -> {
|
||||
symbols.add(symbol);
|
||||
symbolsByDoc.computeIfAbsent(docURI, s -> new ArrayList<SymbolInformation>()).add(symbol);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void extractSymbolInformation(Annotation node, String docURI, AtomicReference<TextDocument> docRef, String content) throws Exception {
|
||||
ITypeBinding typeBinding = node.resolveTypeBinding();
|
||||
|
||||
if (typeBinding != null) {
|
||||
Collection<SymbolProvider> providers = symbolProviders.get(typeBinding);
|
||||
Collection<ITypeBinding> metaAnnotations = AnnotationHierarchies.getMetaAnnotations(typeBinding, symbolProviders::containsKey);
|
||||
if (!providers.isEmpty()) {
|
||||
TextDocument doc = getTempTextDocument(docURI, docRef, content);
|
||||
for (SymbolProvider provider : providers) {
|
||||
Collection<SymbolInformation> sbls = provider.getSymbols(node, typeBinding, metaAnnotations, doc);
|
||||
if (sbls != null) {
|
||||
sbls.forEach(symbol -> {
|
||||
symbols.add(symbol);
|
||||
symbolsByDoc.computeIfAbsent(docURI, s -> new ArrayList<SymbolInformation>()).add(symbol);
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
SymbolInformation symbol = provideDefaultSymbol(node, docURI, docRef, content);
|
||||
if (symbol != null) {
|
||||
symbols.add(symbol);
|
||||
symbolsByDoc.computeIfAbsent(docURI, s -> new ArrayList<SymbolInformation>()).add(symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private TextDocument getTempTextDocument(String docURI, AtomicReference<TextDocument> docRef, String content) throws Exception {
|
||||
TextDocument doc = docRef.get();
|
||||
if (doc == null) {
|
||||
doc = createTempTextDocument(docURI, content);
|
||||
docRef.set(doc);
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
|
||||
private TextDocument createTempTextDocument(String docURI, String content) throws Exception {
|
||||
if (content == null) {
|
||||
Path path = Paths.get(new URI(docURI));
|
||||
content = new String(Files.readAllBytes(path));
|
||||
}
|
||||
|
||||
TextDocument doc = new TextDocument(docURI, LanguageId.PLAINTEXT, 0, content);
|
||||
return doc;
|
||||
}
|
||||
|
||||
private SymbolInformation provideDefaultSymbol(Annotation node, String docURI, AtomicReference<TextDocument> docRef, String content) {
|
||||
try {
|
||||
ITypeBinding type = node.resolveTypeBinding();
|
||||
if (type != null) {
|
||||
String qualifiedName = type.getQualifiedName();
|
||||
if (qualifiedName != null && qualifiedName.startsWith("org.springframework")) {
|
||||
TextDocument doc = getTempTextDocument(docURI, docRef, content);
|
||||
SymbolInformation symbol = new SymbolInformation(node.toString(), SymbolKind.Interface,
|
||||
new Location(doc.getUri(), doc.toRange(node.getStartPosition(), node.getLength())));
|
||||
return symbol;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private String[] getClasspathEntries(IJavaProject project) throws Exception {
|
||||
IClasspath classpath = project.getClasspath();
|
||||
Stream<Path> classpathEntries = classpath.getClasspathEntries().stream();
|
||||
return classpathEntries
|
||||
.filter(path -> path.toFile().exists())
|
||||
.map(path -> path.toAbsolutePath().toString()).toArray(String[]::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* inner class to capture items for the update worker
|
||||
*/
|
||||
private interface WorkerItem {
|
||||
|
||||
public void run();
|
||||
public CompletableFuture<Void> getFuture();
|
||||
|
||||
}
|
||||
|
||||
private class InitializeItem implements WorkerItem {
|
||||
|
||||
private final WorkspaceFolder[] workspaceRoots;
|
||||
private final CompletableFuture<Void> future;
|
||||
|
||||
public InitializeItem(WorkspaceFolder[] workspaceRoots) {
|
||||
log.debug("initialze spring indexer task created for roots: " + Arrays.toString(workspaceRoots));
|
||||
|
||||
this.workspaceRoots = workspaceRoots;
|
||||
this.future = new CompletableFuture<Void>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> getFuture() {
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (!future.isCancelled()) {
|
||||
log.debug("initialze spring indexer task started for roots: " + Arrays.toString(workspaceRoots));
|
||||
|
||||
for (WorkspaceFolder root : workspaceRoots) {
|
||||
SpringIndexer.this.scanFiles(root);
|
||||
}
|
||||
|
||||
log.debug("initialze spring indexer task completed for roots: " + Arrays.toString(workspaceRoots));
|
||||
|
||||
future.complete(null);
|
||||
}
|
||||
else {
|
||||
log.debug("initialze spring indexer task canceled for roots: " + Arrays.toString(workspaceRoots));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class UpdateItem implements WorkerItem {
|
||||
|
||||
private final String docURI;
|
||||
private final String content;
|
||||
private final String[] classpathEntries;
|
||||
|
||||
private final CompletableFuture<Void> future;
|
||||
|
||||
public UpdateItem(String docURI, String content, String[] classpathEntries) {
|
||||
this.docURI = docURI;
|
||||
this.content = content;
|
||||
this.classpathEntries = classpathEntries;
|
||||
this.future = new CompletableFuture<Void>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> getFuture() {
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
SpringIndexer.this.scanFile(docURI, content, classpathEntries);
|
||||
} catch (Exception e) {
|
||||
log.error("{}", e);
|
||||
}
|
||||
future.complete(null);
|
||||
}
|
||||
}
|
||||
|
||||
private class DeleteItem implements WorkerItem {
|
||||
|
||||
private final String docURI;
|
||||
private final CompletableFuture<Void> future;
|
||||
|
||||
public DeleteItem(String docURI) {
|
||||
this.docURI = docURI;
|
||||
this.future = new CompletableFuture<Void>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> getFuture() {
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
List<SymbolInformation> oldSymbols = symbolsByDoc.remove(docURI);
|
||||
if (oldSymbols != null) {
|
||||
symbols.removeAll(oldSymbols);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("{}", e);
|
||||
}
|
||||
future.complete(null);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2018 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.utils;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.ConcurrentSkipListSet;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.BootJavaHoverProvider;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.RunningAppProvider;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.HighlightParams;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class SpringLiveHoverWatchdog {
|
||||
|
||||
|
||||
public static final Duration DEFAULT_INTERVAL = Duration.ofMillis(5000);
|
||||
|
||||
private final long POLLING_INTERVAL_MILLISECONDS;
|
||||
private final Set<String> watchedDocs;
|
||||
private final SimpleLanguageServer server;
|
||||
private final BootJavaHoverProvider hoverProvider;
|
||||
private RunningAppProvider runningAppProvider;
|
||||
|
||||
private boolean highlightsEnabled = true;
|
||||
|
||||
private Timer timer;
|
||||
|
||||
private JavaProjectFinder projectFinder;
|
||||
|
||||
private void refreshEnablement() {
|
||||
boolean shouldEnable = highlightsEnabled && hasInterestingProject(watchedDocs.stream());
|
||||
if (shouldEnable) {
|
||||
start();
|
||||
} else {
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasInterestingProject(Stream<String> uris) {
|
||||
return uris.anyMatch(uri -> projectFinder.find(new TextDocumentIdentifier(uri)).isPresent());
|
||||
}
|
||||
|
||||
public SpringLiveHoverWatchdog(
|
||||
SimpleLanguageServer server,
|
||||
BootJavaHoverProvider hoverProvider,
|
||||
RunningAppProvider runningAppProvider,
|
||||
JavaProjectFinder projectFinder,
|
||||
ProjectObserver projectChanges,
|
||||
Duration pollingInterval
|
||||
) {
|
||||
this.POLLING_INTERVAL_MILLISECONDS = pollingInterval == null ? DEFAULT_INTERVAL.toMillis() : pollingInterval.toMillis();
|
||||
this.server = server;
|
||||
this.hoverProvider = hoverProvider;
|
||||
this.runningAppProvider = runningAppProvider;
|
||||
this.projectFinder = projectFinder;
|
||||
this.watchedDocs = new ConcurrentSkipListSet<>();
|
||||
projectChanges.addListener(new ProjectObserver.Listener() {
|
||||
|
||||
@Override
|
||||
public void deleted(IJavaProject project) {
|
||||
refreshEnablement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void created(IJavaProject project) {
|
||||
refreshEnablement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changed(IJavaProject project) {
|
||||
refreshEnablement();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private synchronized void start() {
|
||||
if (highlightsEnabled && timer == null) {
|
||||
Log.debug("Starting SpringLiveHoverWatchdog");
|
||||
this.timer = new Timer();
|
||||
|
||||
TimerTask task = new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
update();
|
||||
}
|
||||
};
|
||||
|
||||
timer.schedule(task, 0, POLLING_INTERVAL_MILLISECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void shutdown() {
|
||||
if (timer != null) {
|
||||
Log.info("Shutting down SpringLiveHoverWatchdog");
|
||||
timer.cancel();
|
||||
timer = null;
|
||||
watchedDocs.forEach(uri -> cleanupLiveHints(uri));
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void watchDocument(String docURI) {
|
||||
this.watchedDocs.add(docURI);
|
||||
refreshEnablement();
|
||||
}
|
||||
|
||||
public synchronized void unwatchDocument(String docURI) {
|
||||
this.watchedDocs.remove(docURI);
|
||||
cleanupLiveHints(docURI);
|
||||
|
||||
if (watchedDocs.size() == 0) {
|
||||
cleanupResources();
|
||||
}
|
||||
refreshEnablement();
|
||||
}
|
||||
|
||||
public void update(String docURI, SpringBootApp[] runningBootApps) {
|
||||
if (highlightsEnabled) {
|
||||
|
||||
try {
|
||||
if (runningBootApps == null) {
|
||||
runningBootApps = runningAppProvider.getAllRunningSpringApps().toArray(new SpringBootApp[0]);
|
||||
}
|
||||
|
||||
if (runningBootApps != null && runningBootApps.length > 0) {
|
||||
TextDocument doc = this.server.getTextDocumentService().get(docURI);
|
||||
if (doc != null) {
|
||||
Range[] ranges = this.hoverProvider.getLiveHoverHints(doc, runningBootApps);
|
||||
publishLiveHints(docURI, ranges);
|
||||
}
|
||||
}
|
||||
else {
|
||||
cleanupLiveHints(docURI);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void update() {
|
||||
if (this.watchedDocs.size() > 0) {
|
||||
try {
|
||||
SpringBootApp[] runningBootApps = runningAppProvider.getAllRunningSpringApps().toArray(new SpringBootApp[0]);
|
||||
for (String docURI : watchedDocs) {
|
||||
update(docURI, runningBootApps);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void publishLiveHints(String docURI, Range[] ranges) {
|
||||
server.getClient().highlight(new HighlightParams(new TextDocumentIdentifier(docURI), Arrays.asList(ranges)));
|
||||
}
|
||||
|
||||
private void cleanupLiveHints(String docURI) {
|
||||
publishLiveHints(docURI, new Range[0]);
|
||||
}
|
||||
|
||||
private void cleanupResources() {
|
||||
// TODO: close and cleanup open JMX connections and cached data
|
||||
}
|
||||
|
||||
public synchronized void enableHighlights() {
|
||||
if (!highlightsEnabled) {
|
||||
highlightsEnabled = true;
|
||||
refreshEnablement();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void disableHighlights() {
|
||||
if (highlightsEnabled) {
|
||||
highlightsEnabled = false;
|
||||
refreshEnablement();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.ide.vscode.commons.java.IClasspath;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.util.Renderable;
|
||||
import org.springframework.ide.vscode.commons.util.Renderables;
|
||||
|
||||
/**
|
||||
* Helper class, represents parsed info from a Resource, and provide method(s) to
|
||||
* display it somehow.
|
||||
*/
|
||||
public class SpringResource {
|
||||
|
||||
private static final String FILE = "file";
|
||||
private static final String CLASS_PATH_RESOURCE = "class path resource";
|
||||
|
||||
private String type;
|
||||
private String path;
|
||||
private IJavaProject project;
|
||||
|
||||
private static final Pattern BRACKETS = Pattern.compile("\\[[^\\]]*\\]");
|
||||
|
||||
public SpringResource(String toParse, IJavaProject project) {
|
||||
this.project = project;
|
||||
Matcher matcher = BRACKETS.matcher(toParse);
|
||||
if (matcher.find()) {
|
||||
type = toParse.substring(0, matcher.start()).trim();
|
||||
path = toParse.substring(matcher.start()+1, matcher.end()-1);
|
||||
} else {
|
||||
path = toParse;
|
||||
}
|
||||
}
|
||||
|
||||
public String toMarkdown() {
|
||||
if (type==null) {
|
||||
return path; //path is just the raw text in this case
|
||||
}
|
||||
Optional<String> linkUrl;
|
||||
switch (type) {
|
||||
case FILE:
|
||||
String relativePath = projectRelativePath(path);
|
||||
if (relativePath != path && path.endsWith(SourceLinks.CLASS)) {
|
||||
linkUrl = SourceLinks.sourceLinkUrlForClasspathResource(project, relativePath);
|
||||
} else {
|
||||
linkUrl = SourceLinks.sourceLinkForResourcePath(Paths.get(path));
|
||||
}
|
||||
// not a project relative path
|
||||
return linkUrl.isPresent() ? Renderables.link(relativePath, linkUrl.get()).toMarkdown()
|
||||
: "`" + projectRelativePath(path) + "`";
|
||||
case CLASS_PATH_RESOURCE:
|
||||
linkUrl = SourceLinks.sourceLinkUrlForClasspathResource(project, path);
|
||||
return linkUrl.isPresent() ? Renderables.link(path, linkUrl.get()).toMarkdown() : "`"+path+"`";
|
||||
default:
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
private String projectRelativePath(String pathStr) {
|
||||
Path path = Paths.get(pathStr);
|
||||
IClasspath classpath = project.getClasspath();
|
||||
Path outputFolder = classpath.getOutputFolder();
|
||||
if (path.startsWith(outputFolder)) {
|
||||
return outputFolder.relativize(path).toString();
|
||||
}
|
||||
return pathStr;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.value;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class Constants {
|
||||
|
||||
public static final String SPRING_VALUE = "org.springframework.beans.factory.annotation.Value";
|
||||
|
||||
}
|
||||
@@ -1,191 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.value;
|
||||
|
||||
import static org.springframework.ide.vscode.commons.util.StringUtil.camelCaseToHyphens;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.MemberValuePair;
|
||||
import org.eclipse.jdt.core.dom.SimpleName;
|
||||
import org.eclipse.jdt.core.dom.StringLiteral;
|
||||
import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.CompletionProvider;
|
||||
import org.springframework.ide.vscode.boot.java.metadata.SpringPropertyIndexProvider;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
|
||||
import org.springframework.ide.vscode.commons.util.BadLocationException;
|
||||
import org.springframework.ide.vscode.commons.util.FuzzyMap;
|
||||
import org.springframework.ide.vscode.commons.util.FuzzyMap.Match;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class ValueCompletionProcessor implements CompletionProvider {
|
||||
|
||||
private final SpringPropertyIndexProvider indexProvider;
|
||||
|
||||
public ValueCompletionProcessor(SpringPropertyIndexProvider indexProvider) {
|
||||
this.indexProvider = indexProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ICompletionProposal> provideCompletions(ASTNode node, Annotation annotation, ITypeBinding type,
|
||||
int offset, IDocument doc) {
|
||||
|
||||
List<ICompletionProposal> result = new ArrayList<>();
|
||||
|
||||
try {
|
||||
FuzzyMap<ConfigurationMetadataProperty> index = indexProvider.getIndex(doc);
|
||||
|
||||
// case: @Value(<*>)
|
||||
if (node == annotation && doc.get(offset - 1, 2).endsWith("()")) {
|
||||
List<Match<ConfigurationMetadataProperty>> matches = findMatches("", index);
|
||||
|
||||
for (Match<ConfigurationMetadataProperty> match : matches) {
|
||||
|
||||
DocumentEdits edits = new DocumentEdits(doc);
|
||||
edits.replace(offset, offset, "\"${" + match.data.getId() + "}\"");
|
||||
|
||||
ValuePropertyKeyProposal proposal = new ValuePropertyKeyProposal(edits, match.data.getId(), match.data.getName(), null);
|
||||
result.add(proposal);
|
||||
}
|
||||
}
|
||||
// case: @Value(prefix<*>)
|
||||
else if (node instanceof SimpleName && node.getParent() instanceof Annotation) {
|
||||
computeProposalsForSimpleName(node, result, offset, doc, index);
|
||||
}
|
||||
// case: @Value(value=<*>)
|
||||
else if (node instanceof SimpleName && node.getParent() instanceof MemberValuePair
|
||||
&& "value".equals(((MemberValuePair)node.getParent()).getName().toString())) {
|
||||
computeProposalsForSimpleName(node, result, offset, doc, index);
|
||||
}
|
||||
// case: @Value("prefix<*>")
|
||||
else if (node instanceof StringLiteral && node.getParent() instanceof Annotation) {
|
||||
if (node.toString().startsWith("\"") && node.toString().endsWith("\"")) {
|
||||
computeProposalsForStringLiteral(node, result, offset, doc, index);
|
||||
}
|
||||
}
|
||||
// case: @Value(value="prefix<*>")
|
||||
else if (node instanceof StringLiteral && node.getParent() instanceof MemberValuePair
|
||||
&& "value".equals(((MemberValuePair)node.getParent()).getName().toString())) {
|
||||
if (node.toString().startsWith("\"") && node.toString().endsWith("\"")) {
|
||||
computeProposalsForStringLiteral(node, result, offset, doc, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void computeProposalsForSimpleName(ASTNode node, List<ICompletionProposal> completions, int offset,
|
||||
IDocument doc, FuzzyMap<ConfigurationMetadataProperty> index) {
|
||||
String prefix = identifyPropertyPrefix(node.toString(), offset - node.getStartPosition());
|
||||
|
||||
int startOffset = node.getStartPosition();
|
||||
int endOffset = node.getStartPosition() + node.getLength();
|
||||
|
||||
String proposalPrefix = "\"";
|
||||
String proposalPostfix = "\"";
|
||||
|
||||
List<Match<ConfigurationMetadataProperty>> matches = findMatches(prefix, index);
|
||||
|
||||
for (Match<ConfigurationMetadataProperty> match : matches) {
|
||||
|
||||
DocumentEdits edits = new DocumentEdits(doc);
|
||||
edits.replace(startOffset, endOffset, proposalPrefix + "${" + match.data.getId() + "}" + proposalPostfix);
|
||||
|
||||
ValuePropertyKeyProposal proposal = new ValuePropertyKeyProposal(edits, match.data.getId(), match.data.getName(), null);
|
||||
completions.add(proposal);
|
||||
}
|
||||
}
|
||||
|
||||
private void computeProposalsForStringLiteral(ASTNode node, List<ICompletionProposal> completions, int offset,
|
||||
IDocument doc, FuzzyMap<ConfigurationMetadataProperty> index) throws BadLocationException {
|
||||
String prefix = identifyPropertyPrefix(doc.get(node.getStartPosition() + 1, offset - (node.getStartPosition() + 1)), offset - (node.getStartPosition() + 1));
|
||||
|
||||
int startOffset = offset - prefix.length();
|
||||
int endOffset = offset;
|
||||
|
||||
String prePrefix = doc.get(node.getStartPosition() + 1, offset - prefix.length() - node.getStartPosition() - 1);
|
||||
|
||||
String preCompletion;
|
||||
if (prePrefix.endsWith("${")) {
|
||||
preCompletion = "";
|
||||
}
|
||||
else if (prePrefix.endsWith("$")) {
|
||||
preCompletion = "{";
|
||||
}
|
||||
else {
|
||||
preCompletion = "${";
|
||||
}
|
||||
|
||||
String fullNodeContent = doc.get(node.getStartPosition(), node.getLength());
|
||||
String postCompletion = isClosingBracketMissing(fullNodeContent + preCompletion) ? "}" : "";
|
||||
|
||||
List<Match<ConfigurationMetadataProperty>> matches = findMatches(prefix, index);
|
||||
|
||||
for (Match<ConfigurationMetadataProperty> match : matches) {
|
||||
|
||||
DocumentEdits edits = new DocumentEdits(doc);
|
||||
edits.replace(startOffset, endOffset, preCompletion + match.data.getId() + postCompletion);
|
||||
|
||||
ValuePropertyKeyProposal proposal = new ValuePropertyKeyProposal(edits, match.data.getId(), match.data.getName(), null);
|
||||
completions.add(proposal);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isClosingBracketMissing(String fullNodeContent) {
|
||||
int bracketOpens = 0;
|
||||
|
||||
for (int i = 0; i < fullNodeContent.length(); i++) {
|
||||
if (fullNodeContent.charAt(i) == '{') {
|
||||
bracketOpens++;
|
||||
}
|
||||
else if (fullNodeContent.charAt(i) == '}') {
|
||||
bracketOpens--;
|
||||
}
|
||||
}
|
||||
|
||||
return bracketOpens > 0;
|
||||
}
|
||||
|
||||
public String identifyPropertyPrefix(String nodeContent, int offset) {
|
||||
String result = nodeContent.substring(0, offset);
|
||||
|
||||
int i = offset - 1;
|
||||
while (i >= 0) {
|
||||
char c = nodeContent.charAt(i);
|
||||
if (c == '}' || c == '{' || c == '$' || c == '#') {
|
||||
result = result.substring(i + 1, offset);
|
||||
break;
|
||||
}
|
||||
i--;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Match<ConfigurationMetadataProperty>> findMatches(String prefix, FuzzyMap<ConfigurationMetadataProperty> index) {
|
||||
List<Match<ConfigurationMetadataProperty>> matches = index.find(camelCaseToHyphens(prefix));
|
||||
return matches;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,215 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2018 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.value;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.MemberValuePair;
|
||||
import org.eclipse.jdt.core.dom.NodeFinder;
|
||||
import org.eclipse.jdt.core.dom.StringLiteral;
|
||||
import org.eclipse.jdt.core.dom.TypeDeclaration;
|
||||
import org.eclipse.lsp4j.Hover;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.eclipse.lsp4j.jsonrpc.messages.Either;
|
||||
import org.json.JSONObject;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.HoverProvider;
|
||||
import org.springframework.ide.vscode.boot.java.livehover.LiveHoverUtils;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class ValueHoverProvider implements HoverProvider {
|
||||
|
||||
@Override
|
||||
public Hover provideHover(ASTNode node, Annotation annotation, ITypeBinding type, int offset,
|
||||
TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) {
|
||||
|
||||
try {
|
||||
ASTNode exactNode = NodeFinder.perform(node, offset, 0);
|
||||
|
||||
// case: @Value("prefix<*>")
|
||||
if (exactNode != null && exactNode instanceof StringLiteral && exactNode.getParent() instanceof Annotation) {
|
||||
if (exactNode.toString().startsWith("\"") && exactNode.toString().endsWith("\"")) {
|
||||
return provideHover(exactNode.toString(), offset - exactNode.getStartPosition(), exactNode.getStartPosition(), doc, runningApps);
|
||||
}
|
||||
}
|
||||
// case: @Value(value="prefix<*>")
|
||||
else if (exactNode != null && exactNode instanceof StringLiteral && exactNode.getParent() instanceof MemberValuePair
|
||||
&& "value".equals(((MemberValuePair)exactNode.getParent()).getName().toString())) {
|
||||
if (exactNode.toString().startsWith("\"") && exactNode.toString().endsWith("\"")) {
|
||||
return provideHover(exactNode.toString(), offset - exactNode.getStartPosition(), exactNode.getStartPosition(), doc, runningApps);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Range> getLiveHoverHints(Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private Hover provideHover(String value, int offset, int nodeStartOffset, TextDocument doc, SpringBootApp[] runningApps) {
|
||||
|
||||
try {
|
||||
LocalRange range = getPropertyRange(value, offset);
|
||||
if (range != null) {
|
||||
String propertyKey = value.substring(range.getStart(), range.getEnd());
|
||||
|
||||
if (propertyKey != null) {
|
||||
Map<SpringBootApp, JSONObject> allProperties = getPropertiesFromProcesses(runningApps);
|
||||
|
||||
StringBuilder hover = new StringBuilder();
|
||||
|
||||
for (SpringBootApp app : allProperties.keySet()) {
|
||||
JSONObject properties = allProperties.get(app);
|
||||
Iterator<?> keys = properties.keys();
|
||||
while (keys.hasNext()) {
|
||||
String key = (String) keys.next();
|
||||
if (properties.get(key) instanceof JSONObject) {
|
||||
JSONObject props = properties.getJSONObject(key);
|
||||
|
||||
if (props.has(propertyKey)) {
|
||||
String propertyValue = props.getString(propertyKey);
|
||||
|
||||
hover.append(propertyKey + " : " + propertyValue);
|
||||
hover.append(" (from: " + key + ")\n\n");
|
||||
hover.append(LiveHoverUtils.niceAppName(app));
|
||||
hover.append("\n\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hover.length() > 0) {
|
||||
Range hoverRange = doc.toRange(nodeStartOffset + range.getStart(), range.getEnd() - range.getStart());
|
||||
Hover result = new Hover(ImmutableList.of(Either.forLeft(hover.toString())));
|
||||
result.setRange(hoverRange);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Map<SpringBootApp, JSONObject> getPropertiesFromProcesses(SpringBootApp[] runningApps) {
|
||||
Map<SpringBootApp, JSONObject> result = new HashMap<>();
|
||||
|
||||
try {
|
||||
for (SpringBootApp app : runningApps) {
|
||||
String environment = app.getEnvironment();
|
||||
if (environment != null) {
|
||||
JSONObject env = new JSONObject(environment);
|
||||
if (env != null) {
|
||||
result.put(app, env);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public String getPropertyKey(String value, int offset) {
|
||||
LocalRange range = getPropertyRange(value, offset);
|
||||
if (range != null) {
|
||||
return value.substring(range.getStart(), range.getEnd());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public LocalRange getPropertyRange(String value, int offset) {
|
||||
int start = -1;
|
||||
int end = -1;
|
||||
|
||||
for (int i = offset - 1; i >= 0; i--) {
|
||||
if (value.charAt(i) == '{') {
|
||||
start = i + 1;
|
||||
break;
|
||||
}
|
||||
else if (value.charAt(i) == '}') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = offset; i < value.length(); i++) {
|
||||
if (value.charAt(i) == '{' || value.charAt(i) == '$') {
|
||||
break;
|
||||
}
|
||||
else if (value.charAt(i) == '}') {
|
||||
end = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (start > 0 && start < value.length() && end > 0 && end <= value.length() && start < end) {
|
||||
return new LocalRange(start, end);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static class LocalRange {
|
||||
private int start;
|
||||
private int end;
|
||||
|
||||
public LocalRange(int start, int end) {
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
}
|
||||
|
||||
public int getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
public int getEnd() {
|
||||
return end;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Hover provideHover(ASTNode node, TypeDeclaration typeDeclaration, ITypeBinding type, int offset,
|
||||
TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Range> getLiveHoverHints(TypeDeclaration typeDeclaration, TextDocument doc,
|
||||
SpringBootApp[] runningApps) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.value;
|
||||
|
||||
import org.eclipse.lsp4j.CompletionItemKind;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
|
||||
import org.springframework.ide.vscode.commons.util.Renderable;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class ValuePropertyKeyProposal implements ICompletionProposal {
|
||||
|
||||
private DocumentEdits edits;
|
||||
private String label;
|
||||
private String detail;
|
||||
private Renderable documentation;
|
||||
|
||||
public ValuePropertyKeyProposal(DocumentEdits edits, String label, String detail, Renderable documentation) {
|
||||
this.edits = edits;
|
||||
this.label = label;
|
||||
this.detail = detail;
|
||||
this.documentation = documentation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return this.label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletionItemKind getKind() {
|
||||
return CompletionItemKind.Property;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DocumentEdits getTextEdit() {
|
||||
return this.edits;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDetail() {
|
||||
return this.detail;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Renderable getDocumentation() {
|
||||
return this.documentation;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,323 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.value;
|
||||
|
||||
import static org.springframework.ide.vscode.commons.yaml.ast.NodeUtil.asScalar;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.MemberValuePair;
|
||||
import org.eclipse.jdt.core.dom.StringLiteral;
|
||||
import org.eclipse.lsp4j.Location;
|
||||
import org.eclipse.lsp4j.Position;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.ReferenceProvider;
|
||||
import org.springframework.ide.vscode.commons.languageserver.multiroot.WorkspaceFolder;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||
import org.springframework.ide.vscode.commons.util.BadLocationException;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
import org.springframework.ide.vscode.commons.yaml.ast.YamlASTProvider;
|
||||
import org.springframework.ide.vscode.commons.yaml.ast.YamlFileAST;
|
||||
import org.springframework.ide.vscode.commons.yaml.ast.YamlParser;
|
||||
import org.springframework.ide.vscode.java.properties.antlr.parser.AntlrParser;
|
||||
import org.springframework.ide.vscode.java.properties.parser.ParseResults;
|
||||
import org.springframework.ide.vscode.java.properties.parser.Parser;
|
||||
import org.springframework.ide.vscode.java.properties.parser.PropertiesAst.KeyValuePair;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.nodes.MappingNode;
|
||||
import org.yaml.snakeyaml.nodes.Node;
|
||||
import org.yaml.snakeyaml.nodes.NodeId;
|
||||
import org.yaml.snakeyaml.nodes.NodeTuple;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class ValuePropertyReferencesProvider implements ReferenceProvider {
|
||||
|
||||
private SimpleLanguageServer languageServer;
|
||||
|
||||
public ValuePropertyReferencesProvider(SimpleLanguageServer server) {
|
||||
this.languageServer = server;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<? extends Location>> provideReferences(ASTNode node, Annotation annotation,
|
||||
ITypeBinding type, int offset, TextDocument doc) {
|
||||
|
||||
try {
|
||||
// case: @Value("prefix<*>")
|
||||
if (node instanceof StringLiteral && node.getParent() instanceof Annotation) {
|
||||
if (node.toString().startsWith("\"") && node.toString().endsWith("\"")) {
|
||||
return provideReferences(node.toString(), offset - node.getStartPosition(), node.getStartPosition(), doc);
|
||||
}
|
||||
}
|
||||
// case: @Value(value="prefix<*>")
|
||||
else if (node instanceof StringLiteral && node.getParent() instanceof MemberValuePair
|
||||
&& "value".equals(((MemberValuePair)node.getParent()).getName().toString())) {
|
||||
if (node.toString().startsWith("\"") && node.toString().endsWith("\"")) {
|
||||
return provideReferences(node.toString(), offset - node.getStartPosition(), node.getStartPosition(), doc);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private CompletableFuture<List<? extends Location>> provideReferences(String value, int offset, int nodeStartOffset, TextDocument doc) {
|
||||
|
||||
try {
|
||||
LocalRange range = getPropertyRange(value, offset);
|
||||
if (range != null) {
|
||||
String propertyKey = value.substring(range.getStart(), range.getEnd());
|
||||
if (propertyKey != null && propertyKey.length() > 0) {
|
||||
return findReferencesFromPropertyFiles(languageServer.getWorkspaceRoots(), propertyKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public CompletableFuture<List<? extends Location>> findReferencesFromPropertyFiles(
|
||||
Collection<WorkspaceFolder> workspaceRoots,
|
||||
String propertyKey
|
||||
) {
|
||||
for (WorkspaceFolder workspaceFolder : workspaceRoots) {
|
||||
try {
|
||||
Path workspaceRoot = Paths.get(new URI(workspaceFolder.getUri()));
|
||||
try (Stream<Path> walk = Files.walk(workspaceRoot)) {
|
||||
List<Location> locations = walk
|
||||
.filter(path -> isPropertiesFile(path))
|
||||
.filter(path -> path.toFile().isFile())
|
||||
.map(path -> findReferences(path, propertyKey))
|
||||
.flatMap(Collection::stream)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return CompletableFuture.completedFuture(locations);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isPropertiesFile(Path path) {
|
||||
Path fileName = path.getFileName();
|
||||
|
||||
if (fileName.toString().endsWith(".properties") || path.toString().endsWith(".yml")) {
|
||||
return fileName.toString().contains("application");
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private List<Location> findReferences(Path path, String propertyKey) {
|
||||
String filePath = path.toString();
|
||||
if (filePath.endsWith(".properties")) {
|
||||
return findReferencesInPropertiesFile(filePath, propertyKey);
|
||||
}
|
||||
else if (filePath.endsWith(".yml")) {
|
||||
return findReferencesInYMLFile(filePath, propertyKey);
|
||||
}
|
||||
return new ArrayList<Location>();
|
||||
}
|
||||
|
||||
private List<Location> findReferencesInYMLFile(String filePath, String propertyKey) {
|
||||
List<Location> foundLocations = new ArrayList<>();
|
||||
|
||||
try {
|
||||
String fileContent = FileUtils.readFileToString(new File(filePath));
|
||||
|
||||
Yaml yaml = new Yaml();
|
||||
YamlASTProvider parser = new YamlParser(yaml);
|
||||
|
||||
URI docURI = Paths.get(filePath).toUri();
|
||||
TextDocument doc = new TextDocument(docURI.toString(), null);
|
||||
doc.setText(fileContent);
|
||||
YamlFileAST ast = parser.getAST(doc);
|
||||
|
||||
List<Node> nodes = ast.getNodes();
|
||||
if (nodes != null && !nodes.isEmpty()) {
|
||||
for (Node node : nodes) {
|
||||
Node foundNode = findNode(node, "", propertyKey);
|
||||
if (foundNode != null) {
|
||||
|
||||
Position start = new Position();
|
||||
start.setLine(foundNode.getStartMark().getLine());
|
||||
start.setCharacter(foundNode.getStartMark().getColumn());
|
||||
|
||||
Position end = new Position();
|
||||
end.setLine(foundNode.getEndMark().getLine());
|
||||
end.setCharacter(foundNode.getEndMark().getColumn());
|
||||
|
||||
Range range = new Range();
|
||||
range.setStart(start);
|
||||
range.setEnd(end);
|
||||
|
||||
Location location = new Location(docURI.toString(), range);
|
||||
foundLocations.add(location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return foundLocations;
|
||||
}
|
||||
|
||||
protected Node findNode(Node node, String prefix, String propertyKey) {
|
||||
if (node.getNodeId().equals(NodeId.mapping)) {
|
||||
for (NodeTuple entry : ((MappingNode)node).getValue()) {
|
||||
Node keyNode = entry.getKeyNode();
|
||||
String key = asScalar(keyNode);
|
||||
|
||||
String combinedKey = prefix.length() > 0 ? prefix + "." + key : key;
|
||||
|
||||
if (combinedKey != null && combinedKey.equals(propertyKey)) {
|
||||
return keyNode;
|
||||
}
|
||||
else {
|
||||
Node recursive = findNode(entry.getValueNode(), combinedKey, propertyKey);
|
||||
if (recursive != null) {
|
||||
return recursive;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<Location> findReferencesInPropertiesFile(String filePath, String propertyKey) {
|
||||
List<Location> foundLocations = new ArrayList<>();
|
||||
|
||||
try {
|
||||
String fileContent = FileUtils.readFileToString(new File(filePath));
|
||||
|
||||
Parser parser = new AntlrParser();
|
||||
ParseResults parseResults = parser.parse(fileContent);
|
||||
|
||||
if (parseResults != null && parseResults.ast != null) {
|
||||
parseResults.ast.getNodes(KeyValuePair.class).forEach(pair -> {
|
||||
if (pair.getKey() != null && pair.getKey().decode().equals(propertyKey)) {
|
||||
URI docURI = Paths.get(filePath).toUri();
|
||||
TextDocument doc = new TextDocument(docURI.toString(), null);
|
||||
doc.setText(fileContent);
|
||||
|
||||
try {
|
||||
int line = doc.getLineOfOffset(pair.getKey().getOffset());
|
||||
int startInLine = pair.getKey().getOffset() - doc.getLineOffset(line);
|
||||
int endInLine = startInLine + (pair.getKey().getLength());
|
||||
|
||||
Position start = new Position();
|
||||
start.setLine(line);
|
||||
start.setCharacter(startInLine);
|
||||
|
||||
Position end = new Position();
|
||||
end.setLine(line);
|
||||
end.setCharacter(endInLine);
|
||||
|
||||
Range range = new Range();
|
||||
range.setStart(start);
|
||||
range.setEnd(end);
|
||||
|
||||
Location location = new Location(docURI.toString(), range);
|
||||
foundLocations.add(location);
|
||||
|
||||
} catch (BadLocationException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return foundLocations;
|
||||
}
|
||||
|
||||
public LocalRange getPropertyRange(String value, int offset) {
|
||||
int start = -1;
|
||||
int end = -1;
|
||||
|
||||
for (int i = offset - 1; i >= 0; i--) {
|
||||
if (value.charAt(i) == '{') {
|
||||
start = i + 1;
|
||||
break;
|
||||
}
|
||||
else if (value.charAt(i) == '}') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = offset; i < value.length(); i++) {
|
||||
if (value.charAt(i) == '{' || value.charAt(i) == '$') {
|
||||
break;
|
||||
}
|
||||
else if (value.charAt(i) == '}') {
|
||||
end = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (start > 0 && start < value.length() && end > 0 && end <= value.length() && start < end) {
|
||||
return new LocalRange(start, end);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static class LocalRange {
|
||||
private int start;
|
||||
private int end;
|
||||
|
||||
public LocalRange(int start, int end) {
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
}
|
||||
|
||||
public int getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
public int getEnd() {
|
||||
return end;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,367 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2018 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.autowired.test;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
import java.time.Duration;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBeansModel;
|
||||
import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.Editor;
|
||||
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness;
|
||||
import org.springframework.ide.vscode.project.harness.MockRunningAppProvider;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness.CustomizableProjectContent;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness.ProjectCustomizer;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class AutowiredHoverProviderTest {
|
||||
|
||||
private static final ProjectCustomizer FOO_INTERFACE = (CustomizableProjectContent p) -> {
|
||||
p.createType("com.examle.Foo",
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"public interface Foo {\n" +
|
||||
" void doSomeFoo();\n" +
|
||||
"}\n"
|
||||
);
|
||||
|
||||
p.createType("com.examle.DependencyA",
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"public class DependencyA {\n" +
|
||||
"}\n"
|
||||
);
|
||||
|
||||
p.createType("com.examle.DependencyB",
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"public class DependencyB {\n" +
|
||||
"}\n"
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
private BootLanguageServerHarness harness;
|
||||
private ProjectsHarness projects = ProjectsHarness.INSTANCE;
|
||||
|
||||
private MockRunningAppProvider mockAppProvider;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
mockAppProvider = new MockRunningAppProvider();
|
||||
harness = BootLanguageServerHarness.builder()
|
||||
.mockDefaults()
|
||||
.runningAppProvider(mockAppProvider.provider)
|
||||
.watchDogInterval(Duration.ofMillis(100))
|
||||
.build();
|
||||
|
||||
MavenJavaProject jp = projects.mavenProject("empty-boot-15-web-app", FOO_INTERFACE);
|
||||
assertTrue(jp.getClasspath().findType("com.example.Foo").exists());
|
||||
harness.useProject(projects.mavenProject("empty-boot-15-web-app"));
|
||||
harness.intialize(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void componentWithAutomaticallyWiredConstructorInjections() throws Exception {
|
||||
LiveBeansModel beans = LiveBeansModel.builder()
|
||||
.add(LiveBean.builder()
|
||||
.id("autowiredClass")
|
||||
.type("com.example.AutowiredClass")
|
||||
.dependencies("dependencyA", "dependencyB")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("dependencyA")
|
||||
.type("com.example.DependencyA")
|
||||
.fileResource(harness.getOutputFolder() + Paths.get("/com/example/DependencyA.class").toString())
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("dependencyB")
|
||||
.type("com.example.DependencyB")
|
||||
.classpathResource("com/example/DependencyB.class")
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.processId("111")
|
||||
.processName("the-app")
|
||||
.beans(beans)
|
||||
.build();
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.beans.factory.annotation.Autowired;\n" +
|
||||
"import org.springframework.stereotype.Component;\n" +
|
||||
"\n" +
|
||||
"@Component\n" +
|
||||
"public class AutowiredClass {\n" +
|
||||
"\n" +
|
||||
" @Autowired\n" +
|
||||
" public AutowiredClass(DependencyA depA, DependencyB depB) {\n" +
|
||||
" }\n" +
|
||||
"}\n"
|
||||
);
|
||||
|
||||
editor.assertHighlights("@Component", "@Autowired");
|
||||
editor.assertTrimmedHover("@Autowired",
|
||||
"**Injection report for Bean [id: autowiredClass, type: `com.example.AutowiredClass`]**\n" +
|
||||
"\n" +
|
||||
"Process [PID=111, name=`the-app`]:\n" +
|
||||
"\n" +
|
||||
"Bean [id: autowiredClass, type: `com.example.AutowiredClass`] got autowired with:\n" +
|
||||
"\n" +
|
||||
"- Bean: dependencyA \n" +
|
||||
" Type: `com.example.DependencyA` \n" +
|
||||
" Resource: `" + Paths.get("com/example/DependencyA.class") + "`\n" +
|
||||
"- Bean: dependencyB \n" +
|
||||
" Type: `com.example.DependencyB` \n" +
|
||||
" Resource: `com/example/DependencyB.class`\n"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noHoversWhenNoRunningApps() throws Exception {
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.beans.factory.annotation.Autowired;\n" +
|
||||
"import org.springframework.stereotype.Component;\n" +
|
||||
"\n" +
|
||||
"@Component\n" +
|
||||
"public class AutowiredClass {\n" +
|
||||
"\n" +
|
||||
" @Autowired\n" +
|
||||
" public AutowiredClass(DependencyA depA, DependencyB depB) {\n" +
|
||||
" }\n" +
|
||||
"}\n"
|
||||
);
|
||||
|
||||
editor.assertHighlights(/*MONE*/);
|
||||
editor.assertNoHover("@Autowired");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noHoversWhenRunningAppDoesntHaveTheComponent() throws Exception {
|
||||
LiveBeansModel beans = LiveBeansModel.builder()
|
||||
.add(LiveBean.builder()
|
||||
.id("whateverBean")
|
||||
.type("com.example.UnrelatedComponent")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("myController")
|
||||
.type("com.example.UnrelatedComponent")
|
||||
.dependencies("whateverBean")
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.processId("111")
|
||||
.processName("unrelated-app")
|
||||
.beans(beans)
|
||||
.build();
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.beans.factory.annotation.Autowired;\n" +
|
||||
"import org.springframework.stereotype.Component;\n" +
|
||||
"\n" +
|
||||
"@Component\n" +
|
||||
"public class AutowiredClass {\n" +
|
||||
"\n" +
|
||||
" @Autowired\n" +
|
||||
" public AutowiredClass(DependencyA depA, DependencyB depB) {\n" +
|
||||
" }\n" +
|
||||
"}\n"
|
||||
);
|
||||
|
||||
editor.assertHighlights(/*MONE*/);
|
||||
editor.assertNoHover("@Autowired");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noHoversWhenRunningAppDoesntHaveDependenciesForTheAutowiring() throws Exception {
|
||||
LiveBeansModel beans = LiveBeansModel.builder()
|
||||
.add(LiveBean.builder()
|
||||
.id("autowiredClass")
|
||||
.type("com.example.AutowiredClass")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("dependencyA")
|
||||
.type("com.example.DependencyA")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("dependencyB")
|
||||
.type("com.example.DependencyB")
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.processId("111")
|
||||
.processName("the-app")
|
||||
.beans(beans)
|
||||
.build();
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.beans.factory.annotation.Autowired;\n" +
|
||||
"import org.springframework.stereotype.Component;\n" +
|
||||
"\n" +
|
||||
"@Component\n" +
|
||||
"public class AutowiredClass {\n" +
|
||||
"\n" +
|
||||
" @Autowired\n" +
|
||||
" public AutowiredClass(DependencyA depA, DependencyB depB) {\n" +
|
||||
" }\n" +
|
||||
"}\n"
|
||||
);
|
||||
|
||||
editor.assertHighlights("@Component");
|
||||
editor.assertNoHover("@Autowired");
|
||||
}
|
||||
|
||||
@Test public void bug_152553935() throws Exception {
|
||||
//https://www.pivotaltracker.com/story/show/152553935
|
||||
LiveBeansModel beans = LiveBeansModel.builder()
|
||||
.add(LiveBean.builder()
|
||||
.id("defaultFoo")
|
||||
.type("com.example.FooImplementation")
|
||||
.dependencies("defaultFoo")
|
||||
.dependencies("otherBean")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("otherBean")
|
||||
.type("com.example.DependencyA")
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.processId("111")
|
||||
.processName("the-app")
|
||||
.beans(beans)
|
||||
.build();
|
||||
|
||||
|
||||
Editor editor = harness.newEditor(
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.beans.factory.annotation.Autowired;\n" +
|
||||
"import org.springframework.scheduling.TaskScheduler;\n" +
|
||||
"import org.springframework.stereotype.Component;\n" +
|
||||
"\n" +
|
||||
"@Component(\"defaultFoo\")\n" +
|
||||
"public class FooImplementation implements Foo {\n" +
|
||||
" \n" +
|
||||
" private TaskScheduler scheduler;\n" +
|
||||
" \n" +
|
||||
" @Autowired Foo self;\n" +
|
||||
" \n" +
|
||||
" @Override\n" +
|
||||
" public void doSomeFoo() {\n" +
|
||||
" scheduler.scheduleWithFixedDelay(() -> {\n" +
|
||||
" System.out.println(\"Doo Done done!\");\n" +
|
||||
" }, 1000);\n" +
|
||||
" System.out.println(\"Foo do do do do!\");\n" +
|
||||
" }\n" +
|
||||
"\n" +
|
||||
" @Autowired\n" +
|
||||
" public void setScheduler(TaskScheduler scheduler) {\n" +
|
||||
" this.scheduler = scheduler;\n" +
|
||||
" }\n" +
|
||||
"\n" +
|
||||
"}"
|
||||
);
|
||||
editor.assertHighlights("@Component", "@Autowired", "@Autowired");
|
||||
for (int i = 1; i <= 2; i++) {
|
||||
editor.assertHoverContains("@Autowired", i,
|
||||
"Bean [id: defaultFoo, type: `com.example.FooImplementation`] got autowired with:\n" +
|
||||
"\n" +
|
||||
"- Bean: otherBean \n" +
|
||||
" Type: `com.example.DependencyA`");
|
||||
}
|
||||
}
|
||||
|
||||
@Test public void bug_152621242_autowired_constructor_on_a_controller() throws Exception {
|
||||
//https://www.pivotaltracker.com/story/show/152621242
|
||||
LiveBeansModel beans = LiveBeansModel.builder()
|
||||
.add(LiveBean.builder()
|
||||
.id("myController")
|
||||
.type("com.example.MyController")
|
||||
.dependencies("restTemplate")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("restTemplate")
|
||||
.type("org.springframework.web.client.RestTemplate")
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.processId("111")
|
||||
.processName("the-app")
|
||||
.beans(beans)
|
||||
.build();
|
||||
|
||||
|
||||
Editor editor = harness.newEditor(
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.beans.factory.annotation.Autowired;\n" +
|
||||
"import org.springframework.stereotype.Component;\n" +
|
||||
"import org.springframework.stereotype.Controller;\n" +
|
||||
"import org.springframework.web.client.RestTemplate;\n" +
|
||||
"\n" +
|
||||
"@Controller\n" +
|
||||
"public class MyController {\n" +
|
||||
"\n" +
|
||||
" private RestTemplate restClient;\n" +
|
||||
"\n" +
|
||||
" @Autowired\n" +
|
||||
" public MyController(RestTemplate restClient) {\n" +
|
||||
" this.restClient = restClient;\n" +
|
||||
" }\n" +
|
||||
" \n" +
|
||||
"}"
|
||||
);
|
||||
editor.assertHighlights("@Controller", "@Autowired");
|
||||
editor.assertHoverContains("@Autowired",
|
||||
"Bean [id: myController, type: `com.example.MyController`] got autowired with:\n" +
|
||||
"\n" +
|
||||
"- Bean: restTemplate \n" +
|
||||
" Type: `org.springframework.web.client.RestTemplate`"
|
||||
);
|
||||
editor.assertHoverContains("@Controller",
|
||||
"**Injection report for Bean [id: myController, type: `com.example.MyController`]**"
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2018 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.beans.test;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.ide.vscode.boot.java.Annotations;
|
||||
import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchyAwareLookup;
|
||||
import org.springframework.ide.vscode.boot.java.beans.BeansSymbolProvider;
|
||||
import org.springframework.ide.vscode.boot.java.beans.ComponentSymbolProvider;
|
||||
import org.springframework.ide.vscode.boot.java.beans.test.SpringIndexerHarness.TestSymbolInfo;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class SpringIndexerBeansTest {
|
||||
|
||||
private AnnotationHierarchyAwareLookup<SymbolProvider> symbolProviders;
|
||||
private BootLanguageServerHarness harness;
|
||||
private JavaProjectFinder projectFinder;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
symbolProviders = new AnnotationHierarchyAwareLookup<>();
|
||||
symbolProviders.put(Annotations.BEAN, new BeansSymbolProvider());
|
||||
symbolProviders.put(Annotations.COMPONENT, new ComponentSymbolProvider());
|
||||
|
||||
harness = BootLanguageServerHarness.builder().build();
|
||||
projectFinder = harness.getProjectFinder();
|
||||
harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-beans/").toURI()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScanSimpleConfigurationClass() throws Exception {
|
||||
SpringIndexerHarness indexer = new SpringIndexerHarness(harness.getServerWrapper(), projectFinder, symbolProviders);
|
||||
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-beans/").toURI());
|
||||
indexer.initialize(indexer.wsFolder(directory));
|
||||
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/SimpleConfiguration.java").toUri().toString();
|
||||
indexer.assertDocumentSymbols(docUri,
|
||||
symbol("@Configuration", "@+ 'simpleConfiguration' (@Configuration <: @Component) SimpleConfiguration"),
|
||||
symbol("@Bean", "@+ 'simpleBean' (@Bean) BeanClass")
|
||||
);
|
||||
}
|
||||
|
||||
@Test public void testScanSpecialConfigurationClass() throws Exception {
|
||||
SpringIndexerHarness indexer = new SpringIndexerHarness(harness.getServerWrapper(), projectFinder, symbolProviders);
|
||||
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-beans/").toURI());
|
||||
indexer.initialize(indexer.wsFolder(directory));
|
||||
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/SpecialConfiguration.java").toUri().toString();
|
||||
indexer.assertDocumentSymbols(docUri,
|
||||
symbol("@Configuration", "@+ 'specialConfiguration' (@Configuration <: @Component) SpecialConfiguration"),
|
||||
|
||||
// @Bean("implicitNamedBean")
|
||||
symbol("implicitNamedBean", "@+ 'implicitNamedBean' (@Bean) BeanClass"),
|
||||
|
||||
// @Bean(value="valueBean")
|
||||
symbol("valueBean", "@+ 'valueBean' (@Bean) BeanClass"),
|
||||
|
||||
// @Bean(value= {"valueBean1", "valueBean2"})
|
||||
symbol("valueBean1", "@+ 'valueBean1' (@Bean) BeanClass"),
|
||||
symbol("valueBean2", "@+ 'valueBean2' (@Bean) BeanClass"),
|
||||
|
||||
// @Bean(name="namedBean")
|
||||
symbol("namedBean", "@+ 'namedBean' (@Bean) BeanClass"),
|
||||
|
||||
// @Bean(name= {"namedBean1", "namedBean2"})
|
||||
symbol("namedBean1", "@+ 'namedBean1' (@Bean) BeanClass"),
|
||||
symbol("namedBean2", "@+ 'namedBean2' (@Bean) BeanClass")
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScanSimpleComponentClass() throws Exception {
|
||||
SpringIndexerHarness indexer = new SpringIndexerHarness(harness.getServerWrapper(), projectFinder, symbolProviders);
|
||||
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-beans/").toURI());
|
||||
indexer.initialize(indexer.wsFolder(directory));
|
||||
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/SimpleComponent.java").toUri().toString();
|
||||
indexer.assertDocumentSymbols(docUri,
|
||||
symbol("@Component", "@+ 'simpleComponent' (@Component) SimpleComponent")
|
||||
);
|
||||
}
|
||||
|
||||
@Test public void testScanSimpleControllerClass() throws Exception {
|
||||
SpringIndexerHarness indexer = new SpringIndexerHarness(harness.getServerWrapper(), projectFinder, symbolProviders);
|
||||
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-beans/").toURI());
|
||||
indexer.initialize(indexer.wsFolder(directory));
|
||||
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/SimpleController.java").toUri().toString();
|
||||
indexer.assertDocumentSymbols(docUri,
|
||||
symbol("@Controller", "@+ 'simpleController' (@Controller <: @Component) SimpleController")
|
||||
);
|
||||
}
|
||||
|
||||
@Test public void testScanRestControllerClass() throws Exception {
|
||||
SpringIndexerHarness indexer = new SpringIndexerHarness(harness.getServerWrapper(), projectFinder, symbolProviders);
|
||||
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-beans/").toURI());
|
||||
indexer.initialize(indexer.wsFolder(directory));
|
||||
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/SimpleRestController.java").toUri().toString();
|
||||
indexer.assertDocumentSymbols(docUri,
|
||||
symbol("@RestController", "@+ 'simpleRestController' (@RestController <: @Controller, @Component) SimpleRestController")
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////
|
||||
// harness code
|
||||
|
||||
private TestSymbolInfo symbol(String coveredText, String label) {
|
||||
return new TestSymbolInfo(coveredText, label);
|
||||
}
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2018 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.beans.test;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.ide.vscode.boot.java.Annotations;
|
||||
import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchyAwareLookup;
|
||||
import org.springframework.ide.vscode.boot.java.beans.BeansSymbolProvider;
|
||||
import org.springframework.ide.vscode.boot.java.beans.ComponentSymbolProvider;
|
||||
import org.springframework.ide.vscode.boot.java.beans.test.SpringIndexerHarness.TestSymbolInfo;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class SpringIndexerFunctionBeansTest {
|
||||
|
||||
private AnnotationHierarchyAwareLookup<SymbolProvider> symbolProviders;
|
||||
private BootLanguageServerHarness harness;
|
||||
private JavaProjectFinder projectFinder;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
symbolProviders = new AnnotationHierarchyAwareLookup<>();
|
||||
symbolProviders.put(Annotations.BEAN, new BeansSymbolProvider());
|
||||
symbolProviders.put(Annotations.COMPONENT, new ComponentSymbolProvider());
|
||||
|
||||
harness = BootLanguageServerHarness.builder().build();
|
||||
projectFinder = harness.getProjectFinder();
|
||||
harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-beans/").toURI()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScanSimpleFunctionBean() throws Exception {
|
||||
SpringIndexerHarness indexer = new SpringIndexerHarness(harness.getServerWrapper(), projectFinder, symbolProviders);
|
||||
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-beans/").toURI());
|
||||
indexer.initialize(indexer.wsFolder(directory));
|
||||
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/FunctionClass.java").toUri().toString();
|
||||
indexer.assertDocumentSymbols(docUri,
|
||||
symbol("@Configuration", "@+ 'functionClass' (@Configuration <: @Component) FunctionClass"),
|
||||
symbol("@Bean", "@> 'uppercase' (@Bean) Function<String,String>")
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScanSimpleFunctionClass() throws Exception {
|
||||
SpringIndexerHarness indexer = new SpringIndexerHarness(harness.getServerWrapper(), projectFinder, symbolProviders);
|
||||
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-beans/").toURI());
|
||||
indexer.initialize(indexer.wsFolder(directory));
|
||||
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/ScannedFunctionClass.java").toUri().toString();
|
||||
indexer.assertDocumentSymbols(docUri,
|
||||
symbol("ScannedFunctionClass", "@> 'scannedFunctionClass' Function<String,String>")
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScanSpecializedFunctionClass() throws Exception {
|
||||
SpringIndexerHarness indexer = new SpringIndexerHarness(harness.getServerWrapper(), projectFinder, symbolProviders);
|
||||
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-beans/").toURI());
|
||||
indexer.initialize(indexer.wsFolder(directory));
|
||||
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/FunctionFromSpecializedClass.java").toUri().toString();
|
||||
indexer.assertDocumentSymbols(docUri,
|
||||
symbol("FunctionFromSpecializedClass", "@> 'functionFromSpecializedClass' Function<String,String>")
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScanSpecializedFunctionInterface() throws Exception {
|
||||
SpringIndexerHarness indexer = new SpringIndexerHarness(harness.getServerWrapper(), projectFinder, symbolProviders);
|
||||
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-beans/").toURI());
|
||||
indexer.initialize(indexer.wsFolder(directory));
|
||||
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/FunctionFromSpecializedInterface.java").toUri().toString();
|
||||
indexer.assertDocumentSymbols(docUri,
|
||||
symbol("FunctionFromSpecializedInterface", "@> 'functionFromSpecializedInterface' Function<String,String>")
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoSymbolForAbstractClasses() throws Exception {
|
||||
SpringIndexerHarness indexer = new SpringIndexerHarness(harness.getServerWrapper(), projectFinder, symbolProviders);
|
||||
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-beans/").toURI());
|
||||
indexer.initialize(indexer.wsFolder(directory));
|
||||
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/SpecializedFunctionClass.java").toUri().toString();
|
||||
indexer.assertDocumentSymbols(docUri);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoSymbolForSubInterfaces() throws Exception {
|
||||
SpringIndexerHarness indexer = new SpringIndexerHarness(harness.getServerWrapper(), projectFinder, symbolProviders);
|
||||
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-beans/").toURI());
|
||||
indexer.initialize(indexer.wsFolder(directory));
|
||||
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/SpecializedFunctionInterface.java").toUri().toString();
|
||||
indexer.assertDocumentSymbols(docUri);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScanInconsistentInterfaceHierarchy() throws Exception {
|
||||
SpringIndexerHarness indexer = new SpringIndexerHarness(harness.getServerWrapper(), projectFinder, symbolProviders);
|
||||
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-beans/").toURI());
|
||||
indexer.initialize(indexer.wsFolder(directory));
|
||||
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/LoopedFunctionClass.java").toUri().toString();
|
||||
indexer.assertDocumentSymbols(docUri);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
// harness code
|
||||
|
||||
private TestSymbolInfo symbol(String coveredText, String label) {
|
||||
return new TestSymbolInfo(coveredText, label);
|
||||
}
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.beans.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.eclipse.lsp4j.SymbolInformation;
|
||||
import org.springframework.ide.vscode.boot.java.BootJavaLanguageServer;
|
||||
import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchyAwareLookup;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider;
|
||||
import org.springframework.ide.vscode.boot.java.utils.SpringIndexer;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
import org.springframework.ide.vscode.commons.languageserver.multiroot.WorkspaceFolder;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.Editor;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
public class SpringIndexerHarness {
|
||||
|
||||
public static class TestSymbolInfo {
|
||||
|
||||
private String coveredText;
|
||||
private String label;
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((coveredText == null) ? 0 : coveredText.hashCode());
|
||||
result = prime * result + ((label == null) ? 0 : label.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
TestSymbolInfo other = (TestSymbolInfo) obj;
|
||||
if (coveredText == null) {
|
||||
if (other.coveredText != null)
|
||||
return false;
|
||||
} else if (!coveredText.equals(other.coveredText))
|
||||
return false;
|
||||
if (label == null) {
|
||||
if (other.label != null)
|
||||
return false;
|
||||
} else if (!label.equals(other.label))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "["+coveredText+"] => " + label;
|
||||
}
|
||||
|
||||
public TestSymbolInfo(String coveredText, String label) {
|
||||
this.coveredText = coveredText;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final Comparator<Range> RANGE_COMPARATOR = Editor.RANGE_COMPARATOR;
|
||||
private static final Comparator<SymbolInformation> SYMBOL_COMPARATOR = new Comparator<SymbolInformation>() {
|
||||
|
||||
@Override
|
||||
public int compare(SymbolInformation o1, SymbolInformation o2) {
|
||||
int r = o1.getLocation().getUri().compareTo(o2.getLocation().getUri());
|
||||
if (r!=0) return r;
|
||||
|
||||
r = RANGE_COMPARATOR.compare(o1.getLocation().getRange(), o2.getLocation().getRange());
|
||||
if (r!=0) return r;
|
||||
|
||||
return o1.getName().compareTo(o2.getName());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
private SpringIndexer indexer;
|
||||
|
||||
public SpringIndexerHarness(BootJavaLanguageServer server, JavaProjectFinder projectFinder, AnnotationHierarchyAwareLookup<SymbolProvider> symbolProviders) {
|
||||
this.indexer = new SpringIndexer(server, projectFinder, symbolProviders);
|
||||
}
|
||||
|
||||
public void assertDocumentSymbols(String documentUri, TestSymbolInfo... expectedSymbols) throws Exception {
|
||||
List<TestSymbolInfo> actualSymbols = getSymbolsInFile(documentUri);
|
||||
assertEquals(symbolsString(Arrays.asList(expectedSymbols)), symbolsString(actualSymbols));
|
||||
}
|
||||
|
||||
private String symbolsString(List<TestSymbolInfo> symbols) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (TestSymbolInfo s : symbols) {
|
||||
buf.append(s+"\n");
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public List<TestSymbolInfo> getSymbolsInFile(String docURI) throws Exception {
|
||||
List<? extends SymbolInformation> symbols = indexer.getSymbols(docURI);
|
||||
if (symbols!=null) {
|
||||
symbols = new ArrayList<>(symbols);
|
||||
Collections.sort(symbols, SYMBOL_COMPARATOR);
|
||||
TextDocument doc = new TextDocument(docURI, LanguageId.JAVA, 1, IOUtils.toString(new URI(docURI)));
|
||||
ImmutableList.Builder<TestSymbolInfo> symbolInfos = ImmutableList.builder();
|
||||
for (SymbolInformation s : symbols) {
|
||||
int start = doc.toOffset(s.getLocation().getRange().getStart());
|
||||
int end = doc.toOffset(s.getLocation().getRange().getEnd());
|
||||
symbolInfos.add(new TestSymbolInfo(doc.textBetween(start, end), s.getName()));
|
||||
}
|
||||
return symbolInfos.build();
|
||||
}
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
public Collection<WorkspaceFolder> wsFolder(File directory) {
|
||||
if (directory!=null) {
|
||||
return ImmutableList.of(new WorkspaceFolder(
|
||||
directory.toURI().toString(),
|
||||
directory.getName()
|
||||
));
|
||||
}
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
public void initialize(Collection<WorkspaceFolder> wsRoots) {
|
||||
indexer.initialize(wsRoots);
|
||||
}
|
||||
}
|
||||
@@ -1,391 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2018 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.conditionals.test;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.Duration;
|
||||
|
||||
import org.eclipse.lsp4j.Hover;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.ide.vscode.boot.java.BootJavaLanguageServer;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.Editor;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness;
|
||||
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness;
|
||||
import org.springframework.ide.vscode.project.harness.MockRunningAppProvider;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
|
||||
|
||||
public class ConditionalsLiveHoverTest {
|
||||
|
||||
private LanguageServerHarness<BootJavaLanguageServer> harness;
|
||||
private MockRunningAppProvider mockAppProvider;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
|
||||
mockAppProvider = new MockRunningAppProvider();
|
||||
harness = BootLanguageServerHarness.builder()
|
||||
.runningAppProvider(mockAppProvider.provider)
|
||||
.watchDogInterval(Duration.ofMillis(100))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoLiveHoverNoRunningApp() throws Exception {
|
||||
|
||||
File directory = new File(
|
||||
ProjectsHarness.class.getResource("/test-projects/test-conditionals-live-hover/").toURI());
|
||||
String docUri = directory.toPath().resolve("src/main/java/example/ConditionalOnMissingBeanConfig.java").toUri()
|
||||
.toString();
|
||||
|
||||
harness.intialize(directory);
|
||||
|
||||
assertTrue("Expected no mock running boot apps, but found: " + mockAppProvider.mockedApps,
|
||||
mockAppProvider.mockedApps.isEmpty());
|
||||
|
||||
Editor editorWithMethodLiveHover = harness.newEditorFromFileUri(docUri, LanguageId.JAVA);
|
||||
editorWithMethodLiveHover.assertNoHover("@ConditionalOnMissingBean");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLiveHoverConditionalOnBean() throws Exception {
|
||||
|
||||
File directory = new File(
|
||||
ProjectsHarness.class.getResource("/test-projects/test-conditionals-live-hover/").toURI());
|
||||
String docUri = directory.toPath().resolve("src/main/java/example/ConditionalOnBeanConfig.java").toUri()
|
||||
.toString();
|
||||
|
||||
// Build a mock running boot app
|
||||
mockAppProvider.builder().isSpringBootApp(true).port("1111").processId("22022").host("cfapps.io")
|
||||
.processName("test-conditionals-live-hover")
|
||||
.liveConditionalsJson(
|
||||
"{\"positiveMatches\":{\"ConditionalOnBeanConfig#hi\":[{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnBean (types: example.Hello; SearchStrategy: all) found bean 'missing'\"}]}}")
|
||||
.build();
|
||||
|
||||
harness.intialize(directory);
|
||||
|
||||
Editor editor = harness.newEditorFromFileUri(docUri, LanguageId.JAVA);
|
||||
editor.assertHoverContains("@ConditionalOnBean",
|
||||
"@ConditionalOnBean (types: example.Hello; SearchStrategy: all) found bean 'missing'\n" + "\n"
|
||||
+ "Process [PID=22022, name=`test-conditionals-live-hover`]");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLiveHoverConditionalOnMissingBean() throws Exception {
|
||||
|
||||
File directory = new File(
|
||||
ProjectsHarness.class.getResource("/test-projects/test-conditionals-live-hover/").toURI());
|
||||
String docUri = directory.toPath().resolve("src/main/java/example/ConditionalOnMissingBeanConfig.java").toUri()
|
||||
.toString();
|
||||
|
||||
// Build a mock running boot app
|
||||
mockAppProvider.builder().isSpringBootApp(true).port("1111").processId("22022").host("cfapps.io")
|
||||
.processName("test-conditionals-live-hover")
|
||||
.liveConditionalsJson(
|
||||
"{\"positiveMatches\":{\"ConditionalOnMissingBeanConfig#missing\":[{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\"}]}}")
|
||||
.build();
|
||||
|
||||
harness.intialize(directory);
|
||||
|
||||
Editor editor = harness.newEditorFromFileUri(docUri, LanguageId.JAVA);
|
||||
editor.assertHoverContains("@ConditionalOnMissingBean",
|
||||
"@ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\n" + "\n"
|
||||
+ "Process [PID=22022, name=`test-conditionals-live-hover`]");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleLiveHoverContentRealProject() throws Exception {
|
||||
|
||||
File directory = new File(
|
||||
ProjectsHarness.class.getResource("/test-projects/test-conditionals-live-hover/").toURI());
|
||||
String docUri = directory.toPath().resolve("src/main/java/example/MultipleConditionals.java").toUri()
|
||||
.toString();
|
||||
|
||||
// Build a mock running boot app
|
||||
mockAppProvider.builder().isSpringBootApp(true).port("1111").processId("22022").host("cfapps.io")
|
||||
.processName("test-conditionals-live-hover")
|
||||
.liveConditionalsJson(
|
||||
"{\"positiveMatches\":{\"HelloConfig#missing\":[{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\"}],\"HelloConfig2#hi\":[{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnBean (types: example.Hello; SearchStrategy: all) found bean 'missing'\"}],\"MultipleConditionals#hi\":[{\"condition\":\"OnClassCondition\",\"message\":\"@ConditionalOnClass found required class; @ConditionalOnMissingClass did not find unwanted class\"},{\"condition\":\"OnWebApplicationCondition\",\"message\":\"@ConditionalOnWebApplication (required) found StandardServletEnvironment\"},{\"condition\":\"OnJavaCondition\",\"message\":\"@ConditionalOnJava (1.8 or newer) found 1.8\"},{\"condition\":\"OnExpressionCondition\",\"message\":\"@ConditionalOnExpression (#{true}) resulted in true\"},{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnBean (types: example.Hello; SearchStrategy: all) found beans 'hi', 'missing'\"}]}}")
|
||||
.build();
|
||||
|
||||
harness.intialize(directory);
|
||||
|
||||
Editor editor = harness.newEditorFromFileUri(docUri, LanguageId.JAVA);
|
||||
|
||||
editor.assertHoverContains("@ConditionalOnBean",
|
||||
"@ConditionalOnBean (types: example.Hello; SearchStrategy: all) found beans 'hi', 'missing'\n" + "\n"
|
||||
+ "Process [PID=22022, name=`test-conditionals-live-hover`]");
|
||||
|
||||
editor.assertHoverContains("@ConditionalOnWebApplication",
|
||||
"@ConditionalOnWebApplication (required) found StandardServletEnvironment\n" + "\n"
|
||||
+ "Process [PID=22022, name=`test-conditionals-live-hover`]");
|
||||
|
||||
editor.assertHoverContains("@ConditionalOnJava(value=ConditionalOnJava.JavaVersion.EIGHT)",
|
||||
"@ConditionalOnJava (1.8 or newer) found 1.8\n" + "\n"
|
||||
+ "Process [PID=22022, name=`test-conditionals-live-hover`]");
|
||||
|
||||
editor.assertHoverContains("@ConditionalOnMissingClass",
|
||||
"@ConditionalOnClass found required class; @ConditionalOnMissingClass did not find unwanted class\n"
|
||||
+ "\n" + "Process [PID=22022, name=`test-conditionals-live-hover`]");
|
||||
|
||||
editor.assertHoverContains("@ConditionalOnExpression", "@ConditionalOnExpression (#{true}) resulted in true\n"
|
||||
+ "\n" + "Process [PID=22022, name=`test-conditionals-live-hover`]");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleAppInstances() throws Exception {
|
||||
|
||||
// Test that live hover shows information for multiple app instances
|
||||
File directory = new File(
|
||||
ProjectsHarness.class.getResource("/test-projects/test-conditionals-live-hover/").toURI());
|
||||
String docUri = directory.toPath().resolve("src/main/java/example/ConditionalOnMissingBeanConfig.java").toUri()
|
||||
.toString();
|
||||
|
||||
// Build a mock running boot app
|
||||
mockAppProvider.builder().isSpringBootApp(true).port("1000").processId("70000").host("cfapps.io")
|
||||
.processName("test-conditionals-live-hover")
|
||||
.liveConditionalsJson(
|
||||
"{\"positiveMatches\":{\"ConditionalOnMissingBeanConfig#missing\":[{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\"}]}}")
|
||||
.build();
|
||||
|
||||
mockAppProvider.builder().isSpringBootApp(true).port("1001").processId("80000").host("cfapps.io")
|
||||
.processName("test-conditionals-live-hover")
|
||||
.liveConditionalsJson(
|
||||
"{\"positiveMatches\":{\"ConditionalOnMissingBeanConfig#missing\":[{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\"}]}}")
|
||||
.build();
|
||||
|
||||
mockAppProvider.builder().isSpringBootApp(true).port("1002").processId("90000").host("cfapps.io")
|
||||
.processName("test-conditionals-live-hover")
|
||||
.liveConditionalsJson(
|
||||
"{\"positiveMatches\":{\"ConditionalOnMissingBeanConfig#missing\":[{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\"}]}}")
|
||||
.build();
|
||||
|
||||
harness.intialize(directory);
|
||||
|
||||
Editor editor = harness.newEditorFromFileUri(docUri, LanguageId.JAVA);
|
||||
|
||||
editor.assertHoverContains("@ConditionalOnMissingBean",
|
||||
"@ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\n" + "\n"
|
||||
+ "Process [PID=70000, name=`test-conditionals-live-hover`]\n" + "\n" + "---\n" + "\n"
|
||||
+ "@ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\n"
|
||||
+ "\n" + "Process [PID=80000, name=`test-conditionals-live-hover`]\n" + "\n" + "---\n" + "\n"
|
||||
+ "@ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\n"
|
||||
+ "\n" + "Process [PID=90000, name=`test-conditionals-live-hover`]");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleConditionalsSameMethod() throws Exception {
|
||||
|
||||
// Tests something like this:
|
||||
// @Bean
|
||||
// @ConditionalOnBean
|
||||
// @ConditionalOnWebApplication
|
||||
// @ConditionalOnJava(value=ConditionalOnJava.JavaVersion.EIGHT)
|
||||
// @ConditionalOnMissingClass
|
||||
// @ConditionalOnExpression
|
||||
// public Hello hi() {
|
||||
// return null;
|
||||
// }
|
||||
|
||||
File directory = new File(
|
||||
ProjectsHarness.class.getResource("/test-projects/test-conditionals-live-hover/").toURI());
|
||||
String docUri = directory.toPath().resolve("src/main/java/example/MultipleConditionals.java").toUri()
|
||||
.toString();
|
||||
|
||||
// Build a mock running boot app
|
||||
mockAppProvider.builder().isSpringBootApp(true).port("1000").processId("70000").host("cfapps.io")
|
||||
.processName("test-conditionals-live-hover")
|
||||
.liveConditionalsJson(
|
||||
"{\"positiveMatches\":{\"HelloConfig#missing\":[{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\"}],\"HelloConfig2#hi\":[{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnBean (types: example.Hello; SearchStrategy: all) found bean 'missing'\"}],\"MultipleConditionals#hi\":[{\"condition\":\"OnClassCondition\",\"message\":\"@ConditionalOnClass found required class; @ConditionalOnMissingClass did not find unwanted class\"},{\"condition\":\"OnWebApplicationCondition\",\"message\":\"@ConditionalOnWebApplication (required) found StandardServletEnvironment\"},{\"condition\":\"OnJavaCondition\",\"message\":\"@ConditionalOnJava (1.8 or newer) found 1.8\"},{\"condition\":\"OnExpressionCondition\",\"message\":\"@ConditionalOnExpression (#{true}) resulted in true\"},{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnBean (types: example.Hello; SearchStrategy: all) found beans 'hi', 'missing'\"}]}}")
|
||||
.build();
|
||||
|
||||
harness.intialize(directory);
|
||||
|
||||
Editor editor = harness.newEditorFromFileUri(docUri, LanguageId.JAVA);
|
||||
|
||||
// IMPORTANT: test EXACT text to ensure that multiple conditionals on the same
|
||||
// method do not show
|
||||
// up while
|
||||
// hovering over only one of the conditional annotations
|
||||
editor.assertHoverExactText("@ConditionalOnBean",
|
||||
"@ConditionalOnBean (types: example.Hello; SearchStrategy: all) found beans 'hi', 'missing'\n" + "\n"
|
||||
+ "Process [PID=70000, name=`test-conditionals-live-hover`]");
|
||||
|
||||
editor.assertHoverExactText("@ConditionalOnWebApplication",
|
||||
"@ConditionalOnWebApplication (required) found StandardServletEnvironment\n" + "\n"
|
||||
+ "Process [PID=70000, name=`test-conditionals-live-hover`]");
|
||||
|
||||
editor.assertHoverExactText("@ConditionalOnJava(value=ConditionalOnJava.JavaVersion.EIGHT)",
|
||||
"@ConditionalOnJava (1.8 or newer) found 1.8\n" + "\n"
|
||||
+ "Process [PID=70000, name=`test-conditionals-live-hover`]");
|
||||
|
||||
editor.assertHoverExactText("@ConditionalOnMissingClass",
|
||||
"@ConditionalOnClass found required class; @ConditionalOnMissingClass did not find unwanted class\n"
|
||||
+ "\n" + "Process [PID=70000, name=`test-conditionals-live-hover`]");
|
||||
|
||||
editor.assertHoverExactText("@ConditionalOnExpression", "@ConditionalOnExpression (#{true}) resulted in true\n"
|
||||
+ "\n" + "Process [PID=70000, name=`test-conditionals-live-hover`]");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void PT152535713testMultipleLiveHoverHints() throws Exception {
|
||||
|
||||
// Tests fix for PT152535713. Ensure that in a method with multiple
|
||||
// conditionals,
|
||||
// hovering over any one conditional annotation only shows content for that
|
||||
// conditional
|
||||
// and not any of the other ones
|
||||
File directory = new File(
|
||||
ProjectsHarness.class.getResource("/test-projects/test-conditionals-live-hover/").toURI());
|
||||
String docUri = directory.toPath().resolve("src/main/java/example/MultipleConditionalsPT152535713.java").toUri()
|
||||
.toString();
|
||||
|
||||
// Build a mock running boot app
|
||||
mockAppProvider.builder().isSpringBootApp(true).port("1000").processId("70000").host("cfapps.io")
|
||||
.processName("test-conditionals-live-hover")
|
||||
.liveConditionalsJson(
|
||||
"{\"positiveMatches\":{\"MultipleConditionalsPT152535713#hi\":[{\"condition\":\"OnWebApplicationCondition\",\"message\":\"@ConditionalOnWebApplication (required) found StandardServletEnvironment\"},{\"condition\":\"OnJavaCondition\",\"message\":\"@ConditionalOnJava (1.8 or newer) found 1.8\"}]}}")
|
||||
.build();
|
||||
|
||||
harness.intialize(directory);
|
||||
|
||||
Editor editor = harness.newEditorFromFileUri(docUri, LanguageId.JAVA);
|
||||
|
||||
editor.assertHoverExactText("@ConditionalOnWebApplication",
|
||||
"@ConditionalOnWebApplication (required) found StandardServletEnvironment\n" + "\n"
|
||||
+ "Process [PID=70000, name=`test-conditionals-live-hover`]");
|
||||
|
||||
editor.assertHoverExactText("@ConditionalOnJava(value=ConditionalOnJava.JavaVersion.EIGHT)",
|
||||
"@ConditionalOnJava (1.8 or newer) found 1.8\n" + "\n"
|
||||
+ "Process [PID=70000, name=`test-conditionals-live-hover`]");
|
||||
|
||||
// Test that the hovers dont have extra information of the other conditionals:
|
||||
Hover hover = editor.getHover("@ConditionalOnWebApplication");
|
||||
String hoverContent = editor.hoverString(hover);
|
||||
assertFalse(hoverContent.contains("@ConditionalOnJava"));
|
||||
|
||||
hover = editor.getHover("@ConditionalOnJava(value=ConditionalOnJava.JavaVersion.EIGHT)");
|
||||
hoverContent = editor.hoverString(hover);
|
||||
assertFalse(hoverContent.contains("@ConditionalOnWebApplication"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHighlightsMethodConditionals() throws Exception {
|
||||
|
||||
File directory = new File(
|
||||
ProjectsHarness.class.getResource("/test-projects/test-conditionals-live-hover/").toURI());
|
||||
String docUri = directory.toPath().resolve("src/main/java/example/MultipleConditionals.java").toUri()
|
||||
.toString();
|
||||
|
||||
// Build a mock running boot app
|
||||
mockAppProvider.builder().isSpringBootApp(true).port("1111").processId("22022").host("cfapps.io")
|
||||
.processName("test-conditionals-live-hover")
|
||||
.liveConditionalsJson(
|
||||
"{\"positiveMatches\":{\"HelloConfig#missing\":[{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\"}],\"HelloConfig2#hi\":[{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnBean (types: example.Hello; SearchStrategy: all) found bean 'missing'\"}],\"MultipleConditionals#hi\":[{\"condition\":\"OnClassCondition\",\"message\":\"@ConditionalOnClass found required class; @ConditionalOnMissingClass did not find unwanted class\"},{\"condition\":\"OnWebApplicationCondition\",\"message\":\"@ConditionalOnWebApplication (required) found StandardServletEnvironment\"},{\"condition\":\"OnJavaCondition\",\"message\":\"@ConditionalOnJava (1.8 or newer) found 1.8\"},{\"condition\":\"OnExpressionCondition\",\"message\":\"@ConditionalOnExpression (#{true}) resulted in true\"},{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnBean (types: example.Hello; SearchStrategy: all) found beans 'hi', 'missing'\"}]}}")
|
||||
.build();
|
||||
|
||||
harness.intialize(directory);
|
||||
|
||||
String content = "package example;\n" + "\n"
|
||||
+ "import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;\n"
|
||||
+ "import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;\n"
|
||||
+ "import org.springframework.boot.autoconfigure.condition.ConditionalOnJava;\n"
|
||||
+ "import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;\n"
|
||||
+ "import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;\n"
|
||||
+ "import org.springframework.boot.autoconfigure.condition.ConditionalOnNotWebApplication;\n"
|
||||
+ "import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;\n"
|
||||
+ "import org.springframework.context.annotation.Bean;\n"
|
||||
+ "import org.springframework.context.annotation.Configuration;\n" + "\n" + "@Configuration\n"
|
||||
+ "public class MultipleConditionals {\n" + "\n" + " @Bean\n" + " @ConditionalOnBean\n"
|
||||
+ " @ConditionalOnWebApplication\n" + " @ConditionalOnJava(value=ConditionalOnJava.JavaVersion.EIGHT)\n"
|
||||
+ " @ConditionalOnMissingClass\n" + " @ConditionalOnExpression\n" + " public Hello hi() {\n"
|
||||
+ " return null;\n" + " }\n" + "}";
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA, content, docUri);
|
||||
|
||||
editor.assertHighlights("@ConditionalOnBean", "@ConditionalOnWebApplication",
|
||||
"@ConditionalOnJava(value=ConditionalOnJava.JavaVersion.EIGHT)", "@ConditionalOnMissingClass",
|
||||
"@ConditionalOnExpression");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHighlightsTypeConditionals() throws Exception {
|
||||
|
||||
File directory = new File(
|
||||
ProjectsHarness.class.getResource("/test-projects/test-conditionals-live-hover/").toURI());
|
||||
String docUri = directory.toPath().resolve("src/main/java/example/MultipleConditionals.java").toUri()
|
||||
.toString();
|
||||
|
||||
// Build a mock running boot app
|
||||
mockAppProvider.builder().isSpringBootApp(true).port("1111").processId("22022").host("cfapps.io")
|
||||
.processName("test-conditionals-live-hover")
|
||||
.liveConditionalsJson("{\"negativeMatches\": {\n" + " \"MyConditionalComponent\": {\n"
|
||||
+ " \"notMatched\": [\n" + " {\n"
|
||||
+ " \"condition\": \"OnClassCondition\",\n"
|
||||
+ " \"message\": \"@ConditionalOnClass did not find required class 'java.lang.String2'\"\n"
|
||||
+ " }\n" + " ],\n" + " \"matched\": []\n" + " }\n"
|
||||
+ "}\n" + "}")
|
||||
.build();
|
||||
|
||||
harness.intialize(directory);
|
||||
|
||||
String content = "package com.example;\n" + "\n"
|
||||
+ "import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\n"
|
||||
+ "import org.springframework.stereotype.Component;\n" + "\n" + "@Component\n"
|
||||
+ "@ConditionalOnClass(name=\"java.lang.String2\")\n" + "public class MyConditionalComponent {\n" + "}";
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA, content, docUri);
|
||||
|
||||
editor.assertHighlights("@ConditionalOnClass(name=\"java.lang.String2\")");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNegativeMatches() throws Exception {
|
||||
|
||||
File directory = new File(
|
||||
ProjectsHarness.class.getResource("/test-projects/test-conditionals-live-hover/").toURI());
|
||||
String docUri = directory.toPath().resolve("src/main/java/example/MultipleConditionals.java").toUri()
|
||||
.toString();
|
||||
|
||||
// Build a mock running boot app
|
||||
mockAppProvider.builder().isSpringBootApp(true).port("1111").processId("67950").host("cfapps.io")
|
||||
.processName("test-conditionals-live-hover")
|
||||
.liveConditionalsJson("{\"negativeMatches\": {\n" + " \"MyConditionalComponent\": {\n"
|
||||
+ " \"notMatched\": [\n" + " {\n"
|
||||
+ " \"condition\": \"OnClassCondition\",\n"
|
||||
+ " \"message\": \"@ConditionalOnClass did not find required class 'java.lang.String2'\"\n"
|
||||
+ " }\n" + " ],\n" + " \"matched\": []\n" + " }\n"
|
||||
+ "}\n" + "}")
|
||||
.build();
|
||||
|
||||
harness.intialize(directory);
|
||||
|
||||
String content = "package com.example;\n" + "\n"
|
||||
+ "import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\n"
|
||||
+ "import org.springframework.stereotype.Component;\n" + "\n" + "@Component\n"
|
||||
+ "@ConditionalOnClass(name=\"java.lang.String2\")\n" + "public class MyConditionalComponent {\n" + "}";
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA, content, docUri);
|
||||
|
||||
editor.assertHoverContains("@ConditionalOnClass(name=\"java.lang.String2\")",
|
||||
"@ConditionalOnClass did not find required class 'java.lang.String2'\n" + "\n"
|
||||
+ "Process [PID=67950, name=`test-conditionals-live-hover`]");
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.livehover.test;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.Editor;
|
||||
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness;
|
||||
import org.springframework.ide.vscode.project.harness.MockRunningAppProvider;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
|
||||
|
||||
public class ActiveProfilesHoverTest {
|
||||
|
||||
private BootLanguageServerHarness harness;
|
||||
private ProjectsHarness projects = ProjectsHarness.INSTANCE;
|
||||
|
||||
private MockRunningAppProvider mockAppProvider;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
mockAppProvider = new MockRunningAppProvider();
|
||||
harness = BootLanguageServerHarness.builder()
|
||||
.mockDefaults()
|
||||
.runningAppProvider(mockAppProvider.provider)
|
||||
.watchDogInterval(Duration.ofMillis(100))
|
||||
.build();
|
||||
harness.useProject(projects.mavenProject("empty-boot-15-web-app"));
|
||||
harness.intialize(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testActiveProfileHover() throws Exception {
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.processId("22022")
|
||||
.processName("foo.bar.RunningApp")
|
||||
.profiles("testing-profile", "local-profile")
|
||||
.build();
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package hello;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.context.annotation.Configuration;\n" +
|
||||
"import org.springframework.context.annotation.Profile;\n" +
|
||||
"\n" +
|
||||
"@Configuration\n" +
|
||||
"@Profile({\"local-profile\", \"inactive\", \"testing-profile\"})\n" +
|
||||
"public class LocalConfig {\n" +
|
||||
"}"
|
||||
);
|
||||
|
||||
String[] hoverSites = {
|
||||
"@Profile", "local-profile", "testing-profile"
|
||||
};
|
||||
editor.assertHighlights(
|
||||
hoverSites
|
||||
);
|
||||
for (String hoverOver : hoverSites) {
|
||||
editor.assertHoverContains(hoverOver, "testing-profile");
|
||||
editor.assertHoverContains(hoverOver, "local-profile");
|
||||
editor.assertHoverContains(hoverOver, "foo.bar.RunningApp");
|
||||
editor.assertHoverContains(hoverOver, "22022");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testActiveProfileHover_Unknown() throws Exception {
|
||||
//Sometimes its not possible to determine active profiles for an app (e.g. no actuator dependency).
|
||||
//Make sure we show something sensible
|
||||
harness.useProject(projects.mavenProject("no-actuator-boot-15-web-app"));
|
||||
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.processId("22022")
|
||||
.processName("foo.bar.RunningApp")
|
||||
.profilesUnknown()
|
||||
.build();
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package hello;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.context.annotation.Configuration;\n" +
|
||||
"import org.springframework.context.annotation.Profile;\n" +
|
||||
"\n" +
|
||||
"@Configuration\n" +
|
||||
"@Profile(\"local\")\n" +
|
||||
"public class LocalConfig {\n" +
|
||||
"\n" +
|
||||
"}"
|
||||
);
|
||||
editor.assertHoverContains("@Profile", "Consider adding `spring-boot-actuator` as a dependency");
|
||||
editor.assertHighlights(/*NONE*/);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testActiveProfileHoverMixedKnownAndUnknown() throws Exception {
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.processId("22022")
|
||||
.processName("foo.bar.NoActuatorApp")
|
||||
.profilesUnknown()
|
||||
.build();
|
||||
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.processId("3456")
|
||||
.processName("foo.bar.NormalApp")
|
||||
.profiles("fancy")
|
||||
.build();
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package hello;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.context.annotation.Configuration;\n" +
|
||||
"import org.springframework.context.annotation.Profile;\n" +
|
||||
"\n" +
|
||||
"@Configuration\n" +
|
||||
"@Profile({\"unknown\", \"inactive\", \"fancy\"})\n" +
|
||||
"public class LocalConfig {\n" +
|
||||
"\n" +
|
||||
"}"
|
||||
);
|
||||
|
||||
editor.assertHighlights("@Profile", "fancy");
|
||||
editor.assertHoverContains("@Profile", "Unknown");
|
||||
editor.assertHoverContains("@Profile", "fancy");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoRunningApps() throws Exception {
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package hello;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.context.annotation.Configuration;\n" +
|
||||
"import org.springframework.context.annotation.Profile;\n" +
|
||||
"\n" +
|
||||
"@Configuration\n" +
|
||||
"@Profile(\"local\")\n" +
|
||||
"public class LocalConfig {\n" +
|
||||
"\n" +
|
||||
"}"
|
||||
);
|
||||
editor.assertNoHover("@Profile");
|
||||
editor.assertHighlights(/*NONE*/);
|
||||
}
|
||||
}
|
||||
@@ -1,176 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.livehover.test;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.Editor;
|
||||
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness;
|
||||
import org.springframework.ide.vscode.project.harness.MockRunningAppProvider;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
|
||||
|
||||
public class ActuatorWarningHoverTest {
|
||||
|
||||
private static final String ACTUATOR_PROJECT = "empty-boot-15-web-app";
|
||||
private static final String NO_ACTUATOR_PROJECT = "no-actuator-boot-15-web-app";
|
||||
|
||||
private BootLanguageServerHarness harness;
|
||||
private ProjectsHarness projects = ProjectsHarness.INSTANCE;
|
||||
private MockRunningAppProvider mockAppProvider;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
mockAppProvider = new MockRunningAppProvider();
|
||||
harness = BootLanguageServerHarness.builder()
|
||||
.mockDefaults()
|
||||
.runningAppProvider(mockAppProvider.provider)
|
||||
.watchDogInterval(Duration.ofMillis(100))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test public void showWarningIf_NoActuator_and_RunningApp() throws Exception {
|
||||
//Has running app:
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.processId("22022")
|
||||
.processName("foo.bar.RunningApp")
|
||||
.profilesUnknown()
|
||||
.build();
|
||||
|
||||
//No actuator on classpath:
|
||||
String projectName = NO_ACTUATOR_PROJECT;
|
||||
IJavaProject project = projects.mavenProject(projectName);
|
||||
harness.useProject(project);
|
||||
harness.intialize(null);
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package hello;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.context.annotation.Configuration;\n" +
|
||||
"import org.springframework.context.annotation.Profile;\n" +
|
||||
"\n" +
|
||||
"@Configuration\n" +
|
||||
"@Profile({\"local-profile\", \"inactive\", \"testing-profile\"})\n" +
|
||||
"public class LocalConfig {\n" +
|
||||
"}"
|
||||
);
|
||||
|
||||
editor.assertHighlights(/*NONE*/);
|
||||
editor.assertHoverContains("@Profile", "No live hover information");
|
||||
editor.assertHoverContains("@Profile", "Consider adding `spring-boot-actuator` as a dependency to your project `"+projectName+"`");
|
||||
}
|
||||
|
||||
@Test public void noWarningIf_NoRunningApps() throws Exception {
|
||||
|
||||
//No running app:
|
||||
// actaully... no code needed to set that up. mockAppBuilder is 'empty' by default.
|
||||
|
||||
//No actuator on classpath:
|
||||
String projectName = NO_ACTUATOR_PROJECT;
|
||||
IJavaProject project = projects.mavenProject(projectName);
|
||||
harness.useProject(project);
|
||||
harness.intialize(null);
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package hello;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.context.annotation.Configuration;\n" +
|
||||
"import org.springframework.context.annotation.Profile;\n" +
|
||||
"\n" +
|
||||
"@Configuration\n" +
|
||||
"@Profile({\"local-profile\", \"inactive\", \"testing-profile\"})\n" +
|
||||
"public class LocalConfig {\n" +
|
||||
"}"
|
||||
);
|
||||
|
||||
editor.assertHighlights(/*NONE*/);
|
||||
editor.assertNoHover("@Profile");
|
||||
}
|
||||
|
||||
@Test public void noWarningIf_ActuatorOnClasspath() throws Exception {
|
||||
//Has running app:
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.processId("22022")
|
||||
.processName("foo.bar.RunningApp")
|
||||
.profilesUnknown()
|
||||
.build();
|
||||
|
||||
//Actuator on classpath:
|
||||
String projectName = ACTUATOR_PROJECT;
|
||||
IJavaProject project = projects.mavenProject(projectName);
|
||||
harness.useProject(project);
|
||||
harness.intialize(null);
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package hello;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.context.annotation.Bean;\n" +
|
||||
"import org.springframework.context.annotation.Configuration;\n" +
|
||||
"import org.springframework.context.annotation.Profile;\n" +
|
||||
"\n" +
|
||||
"@Configuration\n" +
|
||||
"public class LocalConfig {\n" +
|
||||
" \n" +
|
||||
" @Bean\n" +
|
||||
" Foo myFoo() {\n" +
|
||||
" return new FooImplementation();\n" +
|
||||
" }\n" +
|
||||
"}"
|
||||
);
|
||||
editor.assertHighlights(/*NONE*/);
|
||||
editor.assertNoHover("@Bean");
|
||||
}
|
||||
|
||||
@Test public void warningHoverHasPreciseLocation() throws Exception {
|
||||
// It will be less annoying if limit the area the hover responds to, to just inside the
|
||||
// annotation name rather than the whole range of the ast node.
|
||||
|
||||
//Has running app:
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.processId("22022")
|
||||
.processName("foo.bar.RunningApp")
|
||||
.profilesUnknown()
|
||||
.build();
|
||||
|
||||
//No actuator on classpath:
|
||||
String projectName = NO_ACTUATOR_PROJECT;
|
||||
IJavaProject project = projects.mavenProject(projectName);
|
||||
harness.useProject(project);
|
||||
harness.intialize(null);
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package hello;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.context.annotation.Bean;\n" +
|
||||
"import org.springframework.context.annotation.Configuration;\n" +
|
||||
"import org.springframework.context.annotation.Profile;\n" +
|
||||
"\n" +
|
||||
"@Configuration\n" +
|
||||
"public class LocalConfig {\n" +
|
||||
" \n" +
|
||||
" @Bean(\"the-bean-name\")\n" +
|
||||
" Foo myFoo() {\n" +
|
||||
" return new FooImplementation();\n" +
|
||||
" }\n" +
|
||||
"}"
|
||||
);
|
||||
editor.assertHighlights(/*NONE*/);
|
||||
editor.assertHoverContains("@Bean", "No live hover information");
|
||||
editor.assertNoHover("the-bean-name");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,508 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2018 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.livehover.test;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.Duration;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBeansModel;
|
||||
import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.Editor;
|
||||
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness;
|
||||
import org.springframework.ide.vscode.project.harness.MockRunningAppProvider;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness.CustomizableProjectContent;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness.ProjectCustomizer;
|
||||
|
||||
public class BeanInjectedIntoHoverProviderTest {
|
||||
|
||||
private static final ProjectCustomizer FOO_INTERFACE = (CustomizableProjectContent p) -> {
|
||||
p.createType("hello.Foo",
|
||||
"package hello;\n" +
|
||||
"\n" +
|
||||
"public interface Foo {\n" +
|
||||
" void doSomeFoo();\n" +
|
||||
"}\n"
|
||||
);
|
||||
};
|
||||
|
||||
private BootLanguageServerHarness harness;
|
||||
private ProjectsHarness projects = ProjectsHarness.INSTANCE;
|
||||
|
||||
private MockRunningAppProvider mockAppProvider;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
mockAppProvider = new MockRunningAppProvider();
|
||||
harness = BootLanguageServerHarness.builder()
|
||||
.mockDefaults()
|
||||
.runningAppProvider(mockAppProvider.provider)
|
||||
.watchDogInterval(Duration.ofMillis(100))
|
||||
.build();
|
||||
|
||||
MavenJavaProject jp = projects.mavenProject("empty-boot-15-web-app", FOO_INTERFACE);
|
||||
assertTrue(jp.getClasspath().findType("hello.Foo").exists());
|
||||
harness.useProject(jp);
|
||||
harness.intialize(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void beanWithNoInjections() throws Exception {
|
||||
LiveBeansModel beans = LiveBeansModel.builder()
|
||||
.add(LiveBean.builder()
|
||||
.id("myFoo")
|
||||
.type("hello.FooImplementation")
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.processId("111")
|
||||
.processName("the-app")
|
||||
.beans(beans)
|
||||
.build();
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package hello;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.context.annotation.Bean;\n" +
|
||||
"import org.springframework.context.annotation.Configuration;\n" +
|
||||
"import org.springframework.context.annotation.Profile;\n" +
|
||||
"\n" +
|
||||
"@Configuration\n" +
|
||||
"public class LocalConfig {\n" +
|
||||
" \n" +
|
||||
" @Bean\n" +
|
||||
" Foo myFoo() {\n" +
|
||||
" return new FooImplementation();\n" +
|
||||
" }\n" +
|
||||
"}"
|
||||
);
|
||||
editor.assertHighlights("@Bean");
|
||||
editor.assertTrimmedHover("@Bean",
|
||||
"**Injection report for Bean [id: myFoo]**\n" +
|
||||
"\n" +
|
||||
"Process [PID=111, name=`the-app`]:\n" +
|
||||
"\n" +
|
||||
"Bean [id: myFoo, type: `hello.FooImplementation`] exists but is **Not injected anywhere**\n"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void explicitIdCases() throws Exception {
|
||||
LiveBeansModel beans = LiveBeansModel.builder()
|
||||
.add(LiveBean.builder()
|
||||
.id("beanId")
|
||||
.type("hello.FooImplementation")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("irrelevantBean") //wrong id
|
||||
.type("hello.Foo") //right type (but should be ignored!)
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.processId("111")
|
||||
.processName("the-app")
|
||||
.beans(beans)
|
||||
.build();
|
||||
|
||||
String[] beanAnnotations = {
|
||||
"@Bean(value=\"beanId\")",
|
||||
"@Bean(value=\"beanId\", destroyMethod=\"cleanup\")",
|
||||
"@Bean(value= {\"beanId\", \"alias\"}, destroyMethod=\"cleanup\")",
|
||||
"@Bean(name=\"beanId\")",
|
||||
"@Bean(name=\"beanId\", destroyMethod=\"cleanup\")",
|
||||
"@Bean(name= {\"beanId\", \"alias\"}, destroyMethod=\"cleanup\")",
|
||||
|
||||
"@Bean(\"beanId\")",
|
||||
"@Bean({\"beanId\", \"alias\"})",
|
||||
};
|
||||
for (String beanAnnotation : beanAnnotations) {
|
||||
Editor editor = harness.newEditor(
|
||||
"package hello;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.context.annotation.Bean;\n" +
|
||||
"import org.springframework.context.annotation.Configuration;\n" +
|
||||
"import org.springframework.context.annotation.Profile;\n" +
|
||||
"\n" +
|
||||
"@Configuration\n" +
|
||||
"public class LocalConfig {\n" +
|
||||
" \n" +
|
||||
" "+beanAnnotation+"\n" +
|
||||
" Foo otherFoo() {\n" +
|
||||
" return new FooImplementation();\n" +
|
||||
" }\n" +
|
||||
"}"
|
||||
);
|
||||
editor.assertHighlights("@Bean");
|
||||
editor.assertTrimmedHover("@Bean",
|
||||
"**Injection report for Bean [id: beanId]**\n" +
|
||||
"\n" +
|
||||
"Process [PID=111, name=`the-app`]:\n" +
|
||||
"\n" +
|
||||
"Bean [id: beanId, type: `hello.FooImplementation`] exists but is **Not injected anywhere**\n"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void beanWithOneInjection() throws Exception {
|
||||
LiveBeansModel beans = LiveBeansModel.builder()
|
||||
.add(LiveBean.builder()
|
||||
.id("fooImplementation")
|
||||
.type("hello.FooImplementation")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("myController")
|
||||
.type("hello.MyController")
|
||||
.dependencies("fooImplementation")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("irrelevantBean")
|
||||
.type("com.example.IrrelevantBean")
|
||||
.dependencies("myController")
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.processId("111")
|
||||
.processName("the-app")
|
||||
.beans(beans)
|
||||
.build();
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package hello;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.context.annotation.Bean;\n" +
|
||||
"import org.springframework.context.annotation.Configuration;\n" +
|
||||
"import org.springframework.context.annotation.Profile;\n" +
|
||||
"\n" +
|
||||
"@Configuration\n" +
|
||||
"public class LocalConfig {\n" +
|
||||
" \n" +
|
||||
" @Bean(\"fooImplementation\")\n" +
|
||||
" Foo someFoo() {\n" +
|
||||
" return new FooImplementation();\n" +
|
||||
" }\n" +
|
||||
"}"
|
||||
);
|
||||
editor.assertHighlights("@Bean");
|
||||
editor.assertTrimmedHover("@Bean",
|
||||
"**Injection report for Bean [id: fooImplementation]**\n" +
|
||||
"\n" +
|
||||
"Process [PID=111, name=`the-app`]:\n" +
|
||||
"\n" +
|
||||
"Bean [id: fooImplementation, type: `hello.FooImplementation`] injected into:\n" +
|
||||
"\n" +
|
||||
"- Bean: myController \n" +
|
||||
" Type: `hello.MyController`\n"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void beanFromInnerClassWithOneInjection() throws Exception {
|
||||
LiveBeansModel beans = LiveBeansModel.builder()
|
||||
.add(LiveBean.builder()
|
||||
.id("fooImplementation")
|
||||
.type("hello.FooImplementation")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("myController")
|
||||
.type("hello.MyController")
|
||||
.dependencies("fooImplementation")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("irrelevantBean")
|
||||
.type("com.example.IrrelevantBean")
|
||||
.dependencies("myController")
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.processId("111")
|
||||
.processName("the-app")
|
||||
.beans(beans)
|
||||
.build();
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package hello;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.context.annotation.Bean;\n" +
|
||||
"import org.springframework.context.annotation.Configuration;\n" +
|
||||
"import org.springframework.context.annotation.Profile;\n" +
|
||||
"\n" +
|
||||
"public class OuterClass {\n" +
|
||||
" \n" +
|
||||
" @Configuration\n" +
|
||||
" public static class InnerClass {\n" +
|
||||
" \n" +
|
||||
" @Bean(\"fooImplementation\")\n" +
|
||||
" Foo someFoo() {\n" +
|
||||
" return new FooImplementation();\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
"}"
|
||||
);
|
||||
editor.assertHighlights("@Bean");
|
||||
editor.assertTrimmedHover("@Bean",
|
||||
"**Injection report for Bean [id: fooImplementation]**\n" +
|
||||
"\n" +
|
||||
"Process [PID=111, name=`the-app`]:\n" +
|
||||
"\n" +
|
||||
"Bean [id: fooImplementation, type: `hello.FooImplementation`] injected into:\n" +
|
||||
"\n" +
|
||||
"- Bean: myController \n" +
|
||||
" Type: `hello.MyController`\n"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void beanWithFileResource() throws Exception {
|
||||
Path of = harness.getOutputFolder();
|
||||
LiveBeansModel beans = LiveBeansModel.builder()
|
||||
.add(LiveBean.builder()
|
||||
.id("fooImplementation")
|
||||
.type("hello.FooImplementation")
|
||||
.fileResource("should/not/matter")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("myController")
|
||||
.type("hello.MyController")
|
||||
.fileResource(of+"/hello/MyController.class")
|
||||
.dependencies("fooImplementation")
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.processId("111")
|
||||
.processName("the-app")
|
||||
.beans(beans)
|
||||
.build();
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package hello;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.context.annotation.Bean;\n" +
|
||||
"import org.springframework.context.annotation.Configuration;\n" +
|
||||
"import org.springframework.context.annotation.Profile;\n" +
|
||||
"\n" +
|
||||
"@Configuration\n" +
|
||||
"public class LocalConfig {\n" +
|
||||
" \n" +
|
||||
" @Bean(\"fooImplementation\")\n" +
|
||||
" Foo someFoo() {\n" +
|
||||
" return new FooImplementation();\n" +
|
||||
" }\n" +
|
||||
"}"
|
||||
);
|
||||
editor.assertHighlights("@Bean");
|
||||
editor.assertTrimmedHover("@Bean",
|
||||
"**Injection report for Bean [id: fooImplementation]**\n" +
|
||||
"\n" +
|
||||
"Process [PID=111, name=`the-app`]:\n" +
|
||||
"\n" +
|
||||
"Bean [id: fooImplementation, type: `hello.FooImplementation`] injected into:\n" +
|
||||
"\n" +
|
||||
"- Bean: myController \n" +
|
||||
" Type: `hello.MyController` \n" +
|
||||
" Resource: `" + Paths.get("hello/MyController.class") + "`"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void beanWithClasspathResource() throws Exception {
|
||||
LiveBeansModel beans = LiveBeansModel.builder()
|
||||
.add(LiveBean.builder()
|
||||
.id("fooImplementation")
|
||||
.type("hello.FooImplementation")
|
||||
.fileResource("should/not/matter")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("myController")
|
||||
.type("hello.MyController")
|
||||
.classpathResource("hello/MyController.class")
|
||||
.dependencies("fooImplementation")
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.processId("111")
|
||||
.processName("the-app")
|
||||
.beans(beans)
|
||||
.build();
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package hello;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.context.annotation.Bean;\n" +
|
||||
"import org.springframework.context.annotation.Configuration;\n" +
|
||||
"import org.springframework.context.annotation.Profile;\n" +
|
||||
"\n" +
|
||||
"@Configuration\n" +
|
||||
"public class LocalConfig {\n" +
|
||||
" \n" +
|
||||
" @Bean(\"fooImplementation\")\n" +
|
||||
" Foo someFoo() {\n" +
|
||||
" return new FooImplementation();\n" +
|
||||
" }\n" +
|
||||
"}"
|
||||
);
|
||||
editor.assertHighlights("@Bean");
|
||||
editor.assertTrimmedHover("@Bean",
|
||||
"**Injection report for Bean [id: fooImplementation]**\n" +
|
||||
"\n" +
|
||||
"Process [PID=111, name=`the-app`]:\n" +
|
||||
"\n" +
|
||||
"Bean [id: fooImplementation, type: `hello.FooImplementation`] injected into:\n" +
|
||||
"\n" +
|
||||
"- Bean: myController \n" +
|
||||
" Type: `hello.MyController` \n" +
|
||||
" Resource: `hello/MyController.class`"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void beanWithMultipleInjections() throws Exception {
|
||||
LiveBeansModel beans = LiveBeansModel.builder()
|
||||
.add(LiveBean.builder()
|
||||
.id("fooImplementation")
|
||||
.type("hello.FooImplementation")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("myController")
|
||||
.type("hello.MyController")
|
||||
.dependencies("fooImplementation")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("otherBean")
|
||||
.type("hello.OtherBean")
|
||||
.dependencies("fooImplementation")
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.processId("111")
|
||||
.processName("the-app")
|
||||
.beans(beans)
|
||||
.build();
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package hello;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.context.annotation.Bean;\n" +
|
||||
"import org.springframework.context.annotation.Configuration;\n" +
|
||||
"import org.springframework.context.annotation.Profile;\n" +
|
||||
"\n" +
|
||||
"@Configuration\n" +
|
||||
"public class LocalConfig {\n" +
|
||||
" \n" +
|
||||
" @Bean(\"fooImplementation\")\n" +
|
||||
" Foo someFoo() {\n" +
|
||||
" return new FooImplementation();\n" +
|
||||
" }\n" +
|
||||
"}"
|
||||
);
|
||||
editor.assertHighlights("@Bean");
|
||||
editor.assertTrimmedHover("@Bean",
|
||||
"**Injection report for Bean [id: fooImplementation]**\n" +
|
||||
"\n" +
|
||||
"Process [PID=111, name=`the-app`]:\n" +
|
||||
"\n" +
|
||||
"Bean [id: fooImplementation, type: `hello.FooImplementation`] injected into:\n" +
|
||||
"\n" +
|
||||
"- Bean: myController \n" +
|
||||
" Type: `hello.MyController`\n" +
|
||||
"- Bean: otherBean \n" +
|
||||
" Type: `hello.OtherBean`\n"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noHoversWhenRunningAppDoesntHaveTheBean() throws Exception {
|
||||
LiveBeansModel beans = LiveBeansModel.builder()
|
||||
.add(LiveBean.builder()
|
||||
.id("whateverBean")
|
||||
.type("com.example.UnrelatedBeanType")
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.processId("111")
|
||||
.processName("unrelated-app")
|
||||
.beans(beans)
|
||||
.build();
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package hello;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.context.annotation.Bean;\n" +
|
||||
"import org.springframework.context.annotation.Configuration;\n" +
|
||||
"import org.springframework.context.annotation.Profile;\n" +
|
||||
"\n" +
|
||||
"@Configuration\n" +
|
||||
"public class LocalConfig {\n" +
|
||||
" \n" +
|
||||
" @Bean(\"fooImplementation\")\n" +
|
||||
" Foo someFoo() {\n" +
|
||||
" return new FooImplementation();\n" +
|
||||
" }\n" +
|
||||
"}"
|
||||
);
|
||||
editor.assertHighlights(/*NONE*/);
|
||||
editor.assertNoHover("@Bean");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noHoversWhenNoRunningApps() throws Exception {
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package hello;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.context.annotation.Bean;\n" +
|
||||
"import org.springframework.context.annotation.Configuration;\n" +
|
||||
"import org.springframework.context.annotation.Profile;\n" +
|
||||
"\n" +
|
||||
"@Configuration\n" +
|
||||
"public class LocalConfig {\n" +
|
||||
" \n" +
|
||||
" @Bean(\"fooImplementation\")\n" +
|
||||
" Foo someFoo() {\n" +
|
||||
" return new FooImplementation();\n" +
|
||||
" }\n" +
|
||||
"}"
|
||||
);
|
||||
editor.assertHighlights(/*NONE*/);
|
||||
editor.assertNoHover("@Bean");
|
||||
}
|
||||
}
|
||||
@@ -1,712 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2018 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.livehover.test;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
import java.time.Duration;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBeansModel;
|
||||
import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.Editor;
|
||||
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness;
|
||||
import org.springframework.ide.vscode.project.harness.MockRunningAppProvider;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness.CustomizableProjectContent;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness.ProjectCustomizer;
|
||||
|
||||
public class ComponentInjectionsHoverProviderTest {
|
||||
|
||||
private static final ProjectCustomizer EXTRA_TYPES = (CustomizableProjectContent p) -> {
|
||||
p.createType("com.examle.Foo",
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"public interface Foo {\n" +
|
||||
" void doSomeFoo();\n" +
|
||||
"}\n"
|
||||
);
|
||||
|
||||
p.createType("com.examle.DependencyA",
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"public class DependencyA {\n" +
|
||||
"}\n"
|
||||
);
|
||||
|
||||
p.createType("com.examle.DependencyB",
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"public class DependencyB {\n" +
|
||||
"}\n"
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
private BootLanguageServerHarness harness;
|
||||
private ProjectsHarness projects = ProjectsHarness.INSTANCE;
|
||||
|
||||
private MockRunningAppProvider mockAppProvider;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
mockAppProvider = new MockRunningAppProvider();
|
||||
harness = BootLanguageServerHarness.builder()
|
||||
.mockDefaults()
|
||||
.runningAppProvider(mockAppProvider.provider)
|
||||
.watchDogInterval(Duration.ofMillis(100))
|
||||
.build();
|
||||
|
||||
MavenJavaProject jp = projects.mavenProject("empty-boot-15-web-app", EXTRA_TYPES);
|
||||
assertTrue(jp.getClasspath().findType("com.example.Foo").exists());
|
||||
harness.useProject(jp);
|
||||
harness.intialize(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void componentWithNoInjections() throws Exception {
|
||||
LiveBeansModel beans = LiveBeansModel.builder()
|
||||
.add(LiveBean.builder()
|
||||
.id("fooImplementation")
|
||||
.type("com.example.FooImplementation")
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.processId("111")
|
||||
.processName("the-app")
|
||||
.beans(beans)
|
||||
.build();
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.stereotype.Component;\n" +
|
||||
"\n" +
|
||||
"@Component\n" +
|
||||
"public class FooImplementation implements Foo {\n" +
|
||||
"\n" +
|
||||
" @Override\n" +
|
||||
" public void doSomeFoo() {\n" +
|
||||
" System.out.println(\"Foo do do do!\");\n" +
|
||||
" }\n" +
|
||||
"}\n"
|
||||
);
|
||||
editor.assertHighlights("@Component");
|
||||
editor.assertTrimmedHover("@Component",
|
||||
"**Injection report for Bean [id: fooImplementation, type: `com.example.FooImplementation`]**\n" +
|
||||
"\n" +
|
||||
"Process [PID=111, name=`the-app`]:\n" +
|
||||
"\n" +
|
||||
"Bean [id: fooImplementation, type: `com.example.FooImplementation`] exists but is **Not injected anywhere**\n"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void componentWithOneInjection() throws Exception {
|
||||
LiveBeansModel beans = LiveBeansModel.builder()
|
||||
.add(LiveBean.builder()
|
||||
.id("fooImplementation")
|
||||
.type("com.example.FooImplementation")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("myController")
|
||||
.type("com.example.MyController")
|
||||
.dependencies("fooImplementation")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("irrelevantBean")
|
||||
.type("com.example.IrrelevantBean")
|
||||
.dependencies("myController")
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.processId("111")
|
||||
.processName("the-app")
|
||||
.beans(beans)
|
||||
.build();
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.stereotype.Component;\n" +
|
||||
"\n" +
|
||||
"@Component\n" +
|
||||
"public class FooImplementation implements Foo {\n" +
|
||||
"\n" +
|
||||
" @Override\n" +
|
||||
" public void doSomeFoo() {\n" +
|
||||
" System.out.println(\"Foo do do do!\");\n" +
|
||||
" }\n" +
|
||||
"}\n"
|
||||
);
|
||||
editor.assertHighlights("@Component");
|
||||
editor.assertTrimmedHover("@Component",
|
||||
"**Injection report for Bean [id: fooImplementation, type: `com.example.FooImplementation`]**\n" +
|
||||
"\n" +
|
||||
"Process [PID=111, name=`the-app`]:\n" +
|
||||
"\n" +
|
||||
"Bean [id: fooImplementation, type: `com.example.FooImplementation`] injected into:\n" +
|
||||
"\n" +
|
||||
"- Bean: myController \n" +
|
||||
" Type: `com.example.MyController`"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void componentWithMultipleInjections() throws Exception {
|
||||
LiveBeansModel beans = LiveBeansModel.builder()
|
||||
.add(LiveBean.builder()
|
||||
.id("fooImplementation")
|
||||
.type("com.example.FooImplementation")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("myController")
|
||||
.type("com.example.MyController")
|
||||
.dependencies("fooImplementation")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("otherBean")
|
||||
.type("com.example.OtherBean")
|
||||
.dependencies("fooImplementation")
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.processId("111")
|
||||
.processName("the-app")
|
||||
.beans(beans)
|
||||
.build();
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.stereotype.Component;\n" +
|
||||
"\n" +
|
||||
"@Component\n" +
|
||||
"public class FooImplementation implements Foo {\n" +
|
||||
"\n" +
|
||||
" @Override\n" +
|
||||
" public void doSomeFoo() {\n" +
|
||||
" System.out.println(\"Foo do do do!\");\n" +
|
||||
" }\n" +
|
||||
"}\n"
|
||||
);
|
||||
editor.assertHighlights("@Component");
|
||||
editor.assertTrimmedHover("@Component",
|
||||
"**Injection report for Bean [id: fooImplementation, type: `com.example.FooImplementation`]**\n" +
|
||||
"\n" +
|
||||
"Process [PID=111, name=`the-app`]:\n" +
|
||||
"\n" +
|
||||
"Bean [id: fooImplementation, type: `com.example.FooImplementation`] injected into:\n" +
|
||||
"\n" +
|
||||
"- Bean: myController \n" +
|
||||
" Type: `com.example.MyController`\n" +
|
||||
"- Bean: otherBean \n" +
|
||||
" Type: `com.example.OtherBean`"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void componentWithMultipleInjectionsAndMultipleProcesses() throws Exception {
|
||||
LiveBeansModel beans = LiveBeansModel.builder()
|
||||
.add(LiveBean.builder()
|
||||
.id("fooImplementation")
|
||||
.type("com.example.FooImplementation")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("myController")
|
||||
.type("com.example.MyController")
|
||||
.dependencies("fooImplementation")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("otherBean")
|
||||
.type("com.example.OtherBean")
|
||||
.dependencies("fooImplementation")
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
for (int i = 1; i <= 2; i++) {
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.processId("100"+i)
|
||||
.processName("app-instance-"+i)
|
||||
.beans(beans)
|
||||
.build();
|
||||
}
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.stereotype.Component;\n" +
|
||||
"\n" +
|
||||
"@Component\n" +
|
||||
"public class FooImplementation implements Foo {\n" +
|
||||
"\n" +
|
||||
" @Override\n" +
|
||||
" public void doSomeFoo() {\n" +
|
||||
" System.out.println(\"Foo do do do!\");\n" +
|
||||
" }\n" +
|
||||
"}\n"
|
||||
);
|
||||
editor.assertHighlights("@Component");
|
||||
editor.assertTrimmedHover("@Component",
|
||||
"**Injection report for Bean [id: fooImplementation, type: `com.example.FooImplementation`]**\n" +
|
||||
"\n" +
|
||||
"Process [PID=1001, name=`app-instance-1`]:\n" +
|
||||
"\n" +
|
||||
"Bean [id: fooImplementation, type: `com.example.FooImplementation`] injected into:\n" +
|
||||
"\n" +
|
||||
"- Bean: myController \n" +
|
||||
" Type: `com.example.MyController`\n" +
|
||||
"- Bean: otherBean \n" +
|
||||
" Type: `com.example.OtherBean`\n" +
|
||||
"\n" +
|
||||
"Process [PID=1002, name=`app-instance-2`]:\n" +
|
||||
"\n" +
|
||||
"Bean [id: fooImplementation, type: `com.example.FooImplementation`] injected into:\n" +
|
||||
"\n" +
|
||||
"- Bean: myController \n" +
|
||||
" Type: `com.example.MyController`\n" +
|
||||
"- Bean: otherBean \n" +
|
||||
" Type: `com.example.OtherBean`\n"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onlyShowInfoForRelevantBeanId() throws Exception {
|
||||
LiveBeansModel beans = LiveBeansModel.builder()
|
||||
.add(LiveBean.builder()
|
||||
.id("fooImplementation")
|
||||
.type("com.example.FooImplementation")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("alternateFooImplementation")
|
||||
.type("com.example.FooImplementation")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("myController")
|
||||
.type("com.example.MyController")
|
||||
.dependencies("fooImplementation")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("otherBean")
|
||||
.type("com.example.OtherBean")
|
||||
.dependencies("alternateFooImplementation")
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.processId("111")
|
||||
.processName("the-app")
|
||||
.beans(beans)
|
||||
.build();
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.stereotype.Component;\n" +
|
||||
"\n" +
|
||||
"@Component\n" +
|
||||
"public class FooImplementation implements Foo {\n" +
|
||||
"\n" +
|
||||
" @Override\n" +
|
||||
" public void doSomeFoo() {\n" +
|
||||
" System.out.println(\"Foo do do do!\");\n" +
|
||||
" }\n" +
|
||||
"}\n"
|
||||
);
|
||||
editor.assertHighlights("@Component");
|
||||
editor.assertHoverExactText("@Component",
|
||||
"**Injection report for Bean [id: fooImplementation, type: `com.example.FooImplementation`]**\n" +
|
||||
"\n" +
|
||||
"Process [PID=111, name=`the-app`]:\n" +
|
||||
"\n" +
|
||||
"Bean [id: fooImplementation, type: `com.example.FooImplementation`] injected into:\n" +
|
||||
"\n" +
|
||||
"- Bean: myController \n" +
|
||||
" Type: `com.example.MyController`"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void explicitComponentId() throws Exception {
|
||||
LiveBeansModel beans = LiveBeansModel.builder()
|
||||
.add(LiveBean.builder()
|
||||
.id("fooImplementation")
|
||||
.type("com.example.FooImplementation")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("alternateFooImplementation")
|
||||
.type("com.example.FooImplementation")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("myController")
|
||||
.type("com.example.MyController")
|
||||
.dependencies("fooImplementation")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("otherBean")
|
||||
.type("com.example.OtherBean")
|
||||
.dependencies("alternateFooImplementation")
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.processId("111")
|
||||
.processName("the-app")
|
||||
.beans(beans)
|
||||
.build();
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.stereotype.Component;\n" +
|
||||
"\n" +
|
||||
"@Component(\"alternateFooImplementation\")\n" +
|
||||
"public class FooImplementation implements Foo {\n" +
|
||||
"\n" +
|
||||
" @Override\n" +
|
||||
" public void doSomeFoo() {\n" +
|
||||
" System.out.println(\"Foo do do do!\");\n" +
|
||||
" }\n" +
|
||||
"}\n"
|
||||
);
|
||||
editor.assertHighlights("@Component");
|
||||
editor.assertTrimmedHover("@Component",
|
||||
"**Injection report for Bean [id: alternateFooImplementation, type: `com.example.FooImplementation`]**\n" +
|
||||
"\n" +
|
||||
"Process [PID=111, name=`the-app`]:\n" +
|
||||
"\n" +
|
||||
"Bean [id: alternateFooImplementation, type: `com.example.FooImplementation`] injected into:\n" +
|
||||
"\n" +
|
||||
"- Bean: otherBean \n" +
|
||||
" Type: `com.example.OtherBean`\n"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noHoversWhenRunningAppDoesntHaveTheComponent() throws Exception {
|
||||
LiveBeansModel beans = LiveBeansModel.builder()
|
||||
.add(LiveBean.builder()
|
||||
.id("whateverBean")
|
||||
.type("com.example.UnrelatedComponent")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("myController")
|
||||
.type("com.example.UnrelatedComponent")
|
||||
.dependencies("whateverBean")
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.processId("111")
|
||||
.processName("unrelated-app")
|
||||
.beans(beans)
|
||||
.build();
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.stereotype.Component;\n" +
|
||||
"\n" +
|
||||
"@Component\n" +
|
||||
"public class FooImplementation implements Foo {\n" +
|
||||
"\n" +
|
||||
" @Override\n" +
|
||||
" public void doSomeFoo() {\n" +
|
||||
" System.out.println(\"Foo do do do!\");\n" +
|
||||
" }\n" +
|
||||
"}\n"
|
||||
);
|
||||
editor.assertHighlights(/*MONE*/);
|
||||
editor.assertNoHover("@Component");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noHoversWhenNoRunningApps() throws Exception {
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.stereotype.Component;\n" +
|
||||
"\n" +
|
||||
"@Component\n" +
|
||||
"public class FooImplementation implements Foo {\n" +
|
||||
"\n" +
|
||||
" @Override\n" +
|
||||
" public void doSomeFoo() {\n" +
|
||||
" System.out.println(\"Foo do do do!\");\n" +
|
||||
" }\n" +
|
||||
"}\n"
|
||||
);
|
||||
editor.assertHighlights(/*MONE*/);
|
||||
editor.assertNoHover("@Component");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void componentWithAutomaticallyWiredConstructorInjections() throws Exception {
|
||||
LiveBeansModel beans = LiveBeansModel.builder()
|
||||
.add(LiveBean.builder()
|
||||
.id("autowiredClass")
|
||||
.type("com.example.AutowiredClass")
|
||||
.dependencies("dependencyA", "dependencyB")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("dependencyA")
|
||||
.type("com.example.DependencyA")
|
||||
.fileResource(harness.getOutputFolder() + "/com/example/DependencyA.class")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("dependencyB")
|
||||
.type("com.example.DependencyB")
|
||||
.classpathResource("com/example/DependencyB.class")
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.processId("111")
|
||||
.processName("the-app")
|
||||
.beans(beans)
|
||||
.build();
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.stereotype.Component;\n" +
|
||||
"\n" +
|
||||
"@Component\n" +
|
||||
"public class AutowiredClass {\n" +
|
||||
"\n" +
|
||||
" public AutowiredClass(DependencyA depA, DependencyB depB) {\n" +
|
||||
" }\n" +
|
||||
"}\n"
|
||||
);
|
||||
editor.assertHighlights("@Component");
|
||||
editor.assertTrimmedHover("@Component",
|
||||
"**Injection report for Bean [id: autowiredClass, type: `com.example.AutowiredClass`]**\n" +
|
||||
"\n" +
|
||||
"Process [PID=111, name=`the-app`]:\n" +
|
||||
"\n" +
|
||||
"Bean [id: autowiredClass, type: `com.example.AutowiredClass`] exists but is **Not injected anywhere**\n" +
|
||||
"\n\n" +
|
||||
"Bean [id: autowiredClass, type: `com.example.AutowiredClass`] got autowired with:\n" +
|
||||
"\n" +
|
||||
"- Bean: dependencyA \n" +
|
||||
" Type: `com.example.DependencyA` \n" +
|
||||
" Resource: `" + Paths.get("com/example/DependencyA.class") + "`\n" +
|
||||
"- Bean: dependencyB \n" +
|
||||
" Type: `com.example.DependencyB` \n" +
|
||||
" Resource: `com/example/DependencyB.class`"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void componentWithAutowiredConstructorNoAdditionalHovers() throws Exception {
|
||||
LiveBeansModel beans = LiveBeansModel.builder()
|
||||
.add(LiveBean.builder()
|
||||
.id("autowiredClass")
|
||||
.type("com.example.AutowiredClass")
|
||||
.dependencies("dependencyA", "dependencyB")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("dependencyA")
|
||||
.type("com.example.DependencyA")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("dependencyB")
|
||||
.type("com.example.DependencyB")
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.processId("111")
|
||||
.processName("the-app")
|
||||
.beans(beans)
|
||||
.build();
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.beans.factory.annotation.Autowired;\n" +
|
||||
"import org.springframework.stereotype.Component;\n" +
|
||||
"\n" +
|
||||
"@Component\n" +
|
||||
"public class AutowiredClass {\n" +
|
||||
"\n" +
|
||||
" @Autowired\n" +
|
||||
" public AutowiredClass(DependencyA depA, DependencyB depB) {\n" +
|
||||
" }\n" +
|
||||
"}\n"
|
||||
);
|
||||
editor.assertHighlights("@Component", "@Autowired");
|
||||
editor.assertTrimmedHover("@Component",
|
||||
"**Injection report for Bean [id: autowiredClass, type: `com.example.AutowiredClass`]**\n" +
|
||||
"\n" +
|
||||
"Process [PID=111, name=`the-app`]:\n" +
|
||||
"\n" +
|
||||
"Bean [id: autowiredClass, type: `com.example.AutowiredClass`] exists but is **Not injected anywhere**\n"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bug_153072942_SpringBootApplication__withCGLib_is_a_Component() throws Exception {
|
||||
LiveBeansModel beans = LiveBeansModel.builder()
|
||||
.add(LiveBean.builder()
|
||||
.id("demoApplication")
|
||||
.type("com.example.DemoApplication$$EnhancerBySpringCGLIB$$f378241f")
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.processId("111")
|
||||
.processName("the-app")
|
||||
.beans(beans)
|
||||
.build();
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.boot.SpringApplication;\n" +
|
||||
"import org.springframework.boot.autoconfigure.SpringBootApplication;\n" +
|
||||
"import org.springframework.context.annotation.Bean;\n" +
|
||||
"import org.springframework.web.client.RestTemplate;\n" +
|
||||
"\n" +
|
||||
"@SpringBootApplication\n" +
|
||||
"public class DemoApplication {\n" +
|
||||
" \n" +
|
||||
"\n" +
|
||||
" public static void main(String[] args) {\n" +
|
||||
" SpringApplication.run(DemoApplication.class, args);\n" +
|
||||
" }\n" +
|
||||
"}\n"
|
||||
);
|
||||
editor.assertHighlights("@SpringBootApplication");
|
||||
editor.assertHoverContains("@SpringBootApplication",
|
||||
"**Injection report for Bean [id: demoApplication, type: `com.example.DemoApplication`]**"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void componentFromInnerClass() throws Exception {
|
||||
LiveBeansModel beans = LiveBeansModel.builder()
|
||||
.add(LiveBean.builder()
|
||||
.id("demoApplication.InnerClass")
|
||||
.type("com.example.DemoApplication$InnerClass")
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.processId("111")
|
||||
.processName("the-app")
|
||||
.beans(beans)
|
||||
.build();
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.boot.SpringApplication;\n" +
|
||||
"import org.springframework.boot.autoconfigure.SpringBootApplication;\n" +
|
||||
"import org.springframework.context.annotation.Bean;\n" +
|
||||
"import org.springframework.web.client.RestTemplate;\n" +
|
||||
"\n" +
|
||||
"public class DemoApplication {\n" +
|
||||
" \n" +
|
||||
" @SpringBootApplication\n" +
|
||||
" public static class InnerClass {\n" +
|
||||
" \n" +
|
||||
" public static void main(String[] args) {\n" +
|
||||
" SpringApplication.run(DemoApplication.InnerClass.class, args);\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
"}\n"
|
||||
);
|
||||
editor.assertHighlights("@SpringBootApplication");
|
||||
editor.assertHoverContains("@SpringBootApplication",
|
||||
"**Injection report for Bean [id: demoApplication.InnerClass, type: `com.example.DemoApplication.InnerClass`]**"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void componentFromInnerInnerClass() throws Exception {
|
||||
LiveBeansModel beans = LiveBeansModel.builder()
|
||||
.add(LiveBean.builder()
|
||||
.id("demoApplication.InnerClass.InnerInnerClass")
|
||||
.type("com.example.DemoApplication$InnerClass$InnerInnerClass")
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.processId("111")
|
||||
.processName("the-app")
|
||||
.beans(beans)
|
||||
.build();
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.boot.SpringApplication;\n" +
|
||||
"import org.springframework.boot.autoconfigure.SpringBootApplication;\n" +
|
||||
"import org.springframework.context.annotation.Bean;\n" +
|
||||
"import org.springframework.web.client.RestTemplate;\n" +
|
||||
"\n" +
|
||||
"public class DemoApplication {\n" +
|
||||
" \n" +
|
||||
" public static class InnerClass {\n" +
|
||||
" \n" +
|
||||
" @SpringBootApplication\n" +
|
||||
" public static class InnerInnerClass {\n" +
|
||||
" \n" +
|
||||
" public static void main(String[] args) {\n" +
|
||||
" SpringApplication.run(DemoApplication.InnerClass.InnerInnerClass.class, args);\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
"}\n"
|
||||
);
|
||||
editor.assertHighlights("@SpringBootApplication");
|
||||
editor.assertHoverContains("@SpringBootApplication",
|
||||
"**Injection report for Bean [id: demoApplication.InnerClass.InnerInnerClass, type: `com.example.DemoApplication.InnerClass.InnerInnerClass`]**"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2018 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.livehover.test;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBeansModel;
|
||||
import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.Editor;
|
||||
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness;
|
||||
import org.springframework.ide.vscode.project.harness.MockRunningAppProvider;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
|
||||
|
||||
public class FunctionInjectionsHoverProviderTest {
|
||||
|
||||
private BootLanguageServerHarness harness;
|
||||
private ProjectsHarness projects = ProjectsHarness.INSTANCE;
|
||||
|
||||
private MockRunningAppProvider mockAppProvider;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
mockAppProvider = new MockRunningAppProvider();
|
||||
harness = BootLanguageServerHarness.builder()
|
||||
.mockDefaults()
|
||||
.runningAppProvider(mockAppProvider.provider)
|
||||
.watchDogInterval(Duration.ofMillis(100))
|
||||
.build();
|
||||
|
||||
MavenJavaProject jp = projects.mavenProject("empty-boot-15-web-app");
|
||||
harness.useProject(jp);
|
||||
harness.intialize(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typeButNotAFunction() throws Exception {
|
||||
LiveBeansModel beans = LiveBeansModel.builder()
|
||||
.add(LiveBean.builder()
|
||||
.id("scannedRandomClass")
|
||||
.type("com.example.ScannedRandomClass")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("randomOtherBean")
|
||||
.type("randomOtherBeanType")
|
||||
.dependencies("scannedRandomClass")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("irrelevantBean")
|
||||
.type("com.example.IrrelevantBean")
|
||||
.dependencies("myController")
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.processId("111")
|
||||
.processName("the-app")
|
||||
.beans(beans)
|
||||
.build();
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"import java.io.Serializable;\n" +
|
||||
"\n" +
|
||||
"public class ScannedRandomClass implements Serializable {\n" +
|
||||
"\n" +
|
||||
" public String apply(String t) {\n" +
|
||||
" return t.toUpperCase();\n" +
|
||||
" }\n" +
|
||||
"\n" +
|
||||
"}\n" +
|
||||
""
|
||||
);
|
||||
editor.assertHighlights();
|
||||
editor.assertNoHover("ScannedRandomClass");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void scannedAndInjectedFunction() throws Exception {
|
||||
LiveBeansModel beans = LiveBeansModel.builder()
|
||||
.add(LiveBean.builder()
|
||||
.id("scannedFunctionClass")
|
||||
.type("com.example.ScannedFunctionClass")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("org.springframework.cloud.function.context.config.ContextFunctionCatalogAutoConfiguration")
|
||||
.type("org.springframework.cloud.function.context.config.ContextFunctionCatalogAutoConfiguration")
|
||||
.dependencies("scannedFunctionClass")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("irrelevantBean")
|
||||
.type("com.example.IrrelevantBean")
|
||||
.dependencies("myController")
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.processId("111")
|
||||
.processName("the-app")
|
||||
.beans(beans)
|
||||
.build();
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"import java.util.function.Function;\n" +
|
||||
"\n" +
|
||||
"public class ScannedFunctionClass implements Function<String, String> {\n" +
|
||||
"\n" +
|
||||
" @Override\n" +
|
||||
" public String apply(String t) {\n" +
|
||||
" return t.toUpperCase();\n" +
|
||||
" }\n" +
|
||||
"\n" +
|
||||
"}\n" +
|
||||
""
|
||||
);
|
||||
editor.assertHighlights("ScannedFunctionClass");
|
||||
editor.assertTrimmedHover("ScannedFunctionClass",
|
||||
"**Injection report for Bean [id: scannedFunctionClass, type: `com.example.ScannedFunctionClass`]**\n" +
|
||||
"\n" +
|
||||
"Process [PID=111, name=`the-app`]:\n" +
|
||||
"\n" +
|
||||
"Bean [id: scannedFunctionClass, type: `com.example.ScannedFunctionClass`] injected into:\n" +
|
||||
"\n" +
|
||||
"- Bean: org.springframework.cloud.function.context.config.ContextFunctionCatalogAutoConfiguration \n" +
|
||||
" Type: `org.springframework.cloud.function.context.config.ContextFunctionCatalogAutoConfiguration`"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.references.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import java.net.URI;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.eclipse.lsp4j.Location;
|
||||
import org.junit.Test;
|
||||
import org.springframework.ide.vscode.boot.java.value.ValuePropertyReferencesProvider;
|
||||
import org.springframework.ide.vscode.commons.languageserver.multiroot.WorkspaceFolder;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class PropertyReferenceFinderTest {
|
||||
|
||||
@Test
|
||||
public void testFindReferenceAtBeginningPropFile() throws Exception {
|
||||
ValuePropertyReferencesProvider provider = new ValuePropertyReferencesProvider(null);
|
||||
|
||||
Path root = Paths.get(ProjectsHarness.class.getResource("/test-property-files/simple-case/").toURI());
|
||||
CompletableFuture<List<? extends Location>> resultFuture = provider.findReferencesFromPropertyFiles(wsFolder(root), "test.property");
|
||||
|
||||
assertNotNull(resultFuture);
|
||||
List<? extends Location> locations = resultFuture.get();
|
||||
assertEquals(1, locations.size());
|
||||
Location location = locations.get(0);
|
||||
|
||||
URI docURI = Paths.get(root.toString(), "application.properties").toUri();
|
||||
assertEquals(docURI.toString(), location.getUri());
|
||||
assertEquals(0, location.getRange().getStart().getLine());
|
||||
assertEquals(0, location.getRange().getStart().getCharacter());
|
||||
assertEquals(0, location.getRange().getEnd().getLine());
|
||||
assertEquals(13, location.getRange().getEnd().getCharacter());
|
||||
}
|
||||
|
||||
private Collection<WorkspaceFolder> wsFolder(Path directory) {
|
||||
if (directory!=null) {
|
||||
return ImmutableList.of(new WorkspaceFolder(
|
||||
directory.toUri().toString(),
|
||||
directory.getFileName().toString()
|
||||
));
|
||||
}
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindReferenceAtBeginningYMLFile() throws Exception {
|
||||
ValuePropertyReferencesProvider provider = new ValuePropertyReferencesProvider(null);
|
||||
|
||||
Path root = Paths.get(ProjectsHarness.class.getResource("/test-property-files/simple-yml/").toURI());
|
||||
CompletableFuture<List<? extends Location>> resultFuture = provider.findReferencesFromPropertyFiles(wsFolder(root), "test.property");
|
||||
|
||||
assertNotNull(resultFuture);
|
||||
List<? extends Location> locations = resultFuture.get();
|
||||
assertEquals(1, locations.size());
|
||||
Location location = locations.get(0);
|
||||
|
||||
URI docURI = Paths.get(root.toString(), "application.yml").toUri();
|
||||
assertEquals(docURI.toString(), location.getUri());
|
||||
assertEquals(3, location.getRange().getStart().getLine());
|
||||
assertEquals(2, location.getRange().getStart().getCharacter());
|
||||
assertEquals(3, location.getRange().getEnd().getLine());
|
||||
assertEquals(10, location.getRange().getEnd().getCharacter());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindReferenceWithinTheDocument() throws Exception {
|
||||
ValuePropertyReferencesProvider provider = new ValuePropertyReferencesProvider(null);
|
||||
|
||||
Path root = Paths.get(ProjectsHarness.class.getResource("/test-property-files/simple-case/").toURI());
|
||||
CompletableFuture<List<? extends Location>> resultFuture = provider.findReferencesFromPropertyFiles(wsFolder(root), "server.port");
|
||||
|
||||
assertNotNull(resultFuture);
|
||||
List<? extends Location> locations = resultFuture.get();
|
||||
assertEquals(1, locations.size());
|
||||
Location location = locations.get(0);
|
||||
|
||||
URI docURI = Paths.get(root.toString(), "application.properties").toUri();
|
||||
assertEquals(docURI.toString(), location.getUri());
|
||||
assertEquals(2, location.getRange().getStart().getLine());
|
||||
assertEquals(0, location.getRange().getStart().getCharacter());
|
||||
assertEquals(2, location.getRange().getEnd().getLine());
|
||||
assertEquals(11, location.getRange().getEnd().getCharacter());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindReferenceWithinMultipleFiles() throws Exception {
|
||||
ValuePropertyReferencesProvider provider = new ValuePropertyReferencesProvider(null);
|
||||
|
||||
Path root = Paths.get(ProjectsHarness.class.getResource("/test-property-files/multiple-files/").toURI());
|
||||
CompletableFuture<List<? extends Location>> resultFuture = provider.findReferencesFromPropertyFiles(wsFolder(root), "appl1.prop");
|
||||
|
||||
assertNotNull(resultFuture);
|
||||
List<? extends Location> locations = resultFuture.get();
|
||||
assertEquals(3, locations.size());
|
||||
|
||||
Location location = getLocation(locations, Paths.get(root.toString(), "application-dev.properties").toUri());
|
||||
assertNotNull(location);
|
||||
assertEquals(1, location.getRange().getStart().getLine());
|
||||
assertEquals(0, location.getRange().getStart().getCharacter());
|
||||
assertEquals(1, location.getRange().getEnd().getLine());
|
||||
assertEquals(10, location.getRange().getEnd().getCharacter());
|
||||
|
||||
location = getLocation(locations, Paths.get(root.toString(), "application.properties").toUri());
|
||||
assertNotNull(location);
|
||||
assertEquals(1, location.getRange().getStart().getLine());
|
||||
assertEquals(0, location.getRange().getStart().getCharacter());
|
||||
assertEquals(1, location.getRange().getEnd().getLine());
|
||||
assertEquals(10, location.getRange().getEnd().getCharacter());
|
||||
|
||||
location = getLocation(locations, Paths.get(root.toString(), "prod-application.properties").toUri());
|
||||
assertNotNull(location);
|
||||
assertEquals(1, location.getRange().getStart().getLine());
|
||||
assertEquals(0, location.getRange().getStart().getCharacter());
|
||||
assertEquals(1, location.getRange().getEnd().getLine());
|
||||
assertEquals(10, location.getRange().getEnd().getCharacter());
|
||||
}
|
||||
|
||||
private Location getLocation(List<? extends Location> locations, URI docURI) {
|
||||
for (Location location : locations) {
|
||||
if (docURI.toString().equals(location.getUri())) {
|
||||
return location;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindReferenceWithinMultipleMixedFiles() throws Exception {
|
||||
ValuePropertyReferencesProvider provider = new ValuePropertyReferencesProvider(null);
|
||||
|
||||
Path root = Paths.get(ProjectsHarness.class.getResource("/test-property-files/mixed-multiple-files/").toURI());
|
||||
CompletableFuture<List<? extends Location>> resultFuture = provider.findReferencesFromPropertyFiles(wsFolder(root), "appl1.prop");
|
||||
|
||||
assertNotNull(resultFuture);
|
||||
List<? extends Location> locations = resultFuture.get();
|
||||
assertEquals(2, locations.size());
|
||||
|
||||
Location location = getLocation(locations, Paths.get(root.toString(), "application-dev.properties").toUri());
|
||||
assertNotNull(location);
|
||||
assertEquals(1, location.getRange().getStart().getLine());
|
||||
assertEquals(0, location.getRange().getStart().getCharacter());
|
||||
assertEquals(1, location.getRange().getEnd().getLine());
|
||||
assertEquals(10, location.getRange().getEnd().getCharacter());
|
||||
|
||||
location = getLocation(locations, Paths.get(root.toString(), "application.yml").toUri());
|
||||
assertNotNull(locations);
|
||||
assertEquals(3, location.getRange().getStart().getLine());
|
||||
assertEquals(2, location.getRange().getStart().getCharacter());
|
||||
assertEquals(3, location.getRange().getEnd().getLine());
|
||||
assertEquals(6, location.getRange().getEnd().getCharacter());
|
||||
}
|
||||
}
|
||||
@@ -1,809 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2018 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.requestmapping.test;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.Duration;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.ide.vscode.boot.java.BootJavaLanguageServer;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.Editor;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness;
|
||||
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness;
|
||||
import org.springframework.ide.vscode.project.harness.MockRunningAppProvider;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
|
||||
|
||||
public class RequestMappingLiveHoverTest {
|
||||
|
||||
private LanguageServerHarness<BootJavaLanguageServer> harness;
|
||||
private MockRunningAppProvider mockAppProvider;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
|
||||
mockAppProvider = new MockRunningAppProvider();
|
||||
harness = BootLanguageServerHarness.builder()
|
||||
.runningAppProvider(mockAppProvider.provider)
|
||||
.watchDogInterval(Duration.ofMillis(100))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLiveHoverHintTypeMapping() throws Exception {
|
||||
|
||||
File directory = new File(
|
||||
ProjectsHarness.class.getResource("/test-projects/test-request-mapping-live-hover/").toURI());
|
||||
String docUri = directory.toPath().resolve("src/main/java/example/HelloWorldController.java").toUri()
|
||||
.toString();
|
||||
|
||||
// Build a mock running boot app
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.port("1111")
|
||||
.processId("22022")
|
||||
.host("cfapps.io")
|
||||
.processName("test-request-mapping-live-hover")
|
||||
// Ugly, but this is real JSON copied from a real live running app. We want the
|
||||
// mock app to return realistic results if possible
|
||||
.requestMappings(
|
||||
"{\"/webjars/**\":{\"bean\":\"resourceHandlerMapping\"},\"/**\":{\"bean\":\"resourceHandlerMapping\"},\"/**/favicon.ico\":{\"bean\":\"faviconHandlerMapping\"},\"{[/hello-world],methods=[GET]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public example.Greeting example.HelloWorldController.sayHello(java.lang.String)\"},\"{[/goodbye]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public java.lang.String example.RestApi.goodbye()\"},\"{[/hello]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public java.lang.String example.RestApi.hello()\"},\"{[/error]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)\"},\"{[/error],produces=[text/html]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)\"}}")
|
||||
.build();
|
||||
|
||||
harness.intialize(directory);
|
||||
|
||||
Editor editor = harness.newEditorFromFileUri(docUri, LanguageId.JAVA);
|
||||
editor.assertHighlights("@RequestMapping(method=RequestMethod.GET)");
|
||||
editor.assertHoverContains("@RequestMapping(method=RequestMethod.GET)", "[http://cfapps.io:1111/hello-world](http://cfapps.io:1111/hello-world)\n" +
|
||||
"\n" +
|
||||
"Process [PID=22022, name=`test-request-mapping-live-hover`]");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLiveHoverHintMethod() throws Exception {
|
||||
|
||||
File directory = new File(
|
||||
ProjectsHarness.class.getResource("/test-projects/test-request-mapping-live-hover/").toURI());
|
||||
String docUri = directory.toPath().resolve("src/main/java/example/RestApi.java").toUri()
|
||||
.toString();
|
||||
|
||||
|
||||
// Build a mock running boot app
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.port("999")
|
||||
.processId("76543")
|
||||
.host("cfapps.io")
|
||||
.processName("test-request-mapping-live-hover")
|
||||
// Ugly, but this is real JSON copied from a real live running app. We want the
|
||||
// mock app to return realistic results if possible
|
||||
.requestMappings(
|
||||
"{\"/webjars/**\":{\"bean\":\"resourceHandlerMapping\"},\"/**\":{\"bean\":\"resourceHandlerMapping\"},\"/**/favicon.ico\":{\"bean\":\"faviconHandlerMapping\"},\"{[/hello-world],methods=[GET]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public example.Greeting example.HelloWorldController.sayHello(java.lang.String)\"},\"{[/goodbye]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public java.lang.String example.RestApi.goodbye()\"},\"{[/hello]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public java.lang.String example.RestApi.hello()\"},\"{[/error]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)\"},\"{[/error],produces=[text/html]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)\"}}")
|
||||
. build();
|
||||
|
||||
harness.intialize(directory);
|
||||
|
||||
Editor editor = harness.newEditorFromFileUri(docUri, LanguageId.JAVA);
|
||||
editor.assertHighlights("@RequestMapping(\"/hello\")", "@RequestMapping(\"/goodbye\")");
|
||||
|
||||
editor.assertHoverContains("@RequestMapping(\"/hello\")", "[http://cfapps.io:999/hello](http://cfapps.io:999/hello)\n" +
|
||||
"\n" +
|
||||
"Process [PID=76543, name=`test-request-mapping-live-hover`]");
|
||||
|
||||
editor.assertHoverContains("@RequestMapping(\"/goodbye\")", "[http://cfapps.io:999/goodbye](http://cfapps.io:999/goodbye)\n" +
|
||||
"\n" +
|
||||
"Process [PID=76543, name=`test-request-mapping-live-hover`]");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoLiveHoverNoRunningApp() throws Exception {
|
||||
|
||||
File directory = new File(
|
||||
ProjectsHarness.class.getResource("/test-projects/test-request-mapping-live-hover/").toURI());
|
||||
String docUri = directory.toPath().resolve("src/main/java/example/RestApi.java").toUri()
|
||||
.toString();
|
||||
|
||||
harness.intialize(directory);
|
||||
|
||||
assertTrue("Expected no mock running boot apps, but found: " + mockAppProvider.mockedApps,
|
||||
mockAppProvider.mockedApps.isEmpty());
|
||||
|
||||
Editor editorWithMethodLiveHover = harness.newEditorFromFileUri(docUri, LanguageId.JAVA);
|
||||
editorWithMethodLiveHover.assertNoHover("@RequestMapping(\"/hello\")");
|
||||
|
||||
editorWithMethodLiveHover.assertNoHover("@RequestMapping(\"/goodbye\")");
|
||||
|
||||
docUri = directory.toPath().resolve("src/main/java/example/HelloWorldController.java").toUri()
|
||||
.toString();
|
||||
|
||||
Editor editorWithTypeLiveHover = harness.newEditorFromFileUri(docUri, LanguageId.JAVA);
|
||||
editorWithTypeLiveHover.assertNoHover("@RequestMapping(\"/hello-world\")");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleMethodHoverHintMethod1() throws Exception {
|
||||
|
||||
File directory = new File(
|
||||
ProjectsHarness.class.getResource("/test-projects/test-request-mapping-live-hover/").toURI());
|
||||
String docUri = directory.toPath().resolve("src/main/java/example/RestApi.java").toUri()
|
||||
.toString();
|
||||
|
||||
|
||||
// Build a mock running boot app
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.port("999")
|
||||
.processId("76543")
|
||||
.host("cfapps.io")
|
||||
.processName("test-request-mapping-live-hover")
|
||||
// Ugly, but this is real JSON copied from a real live running app. We want the
|
||||
// mock app to return realistic results if possible
|
||||
.requestMappings(
|
||||
"{\"{[/greetings],methods=[DELETE]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public void com.example.RestApi.deleteGreetings()\"}}")
|
||||
. build();
|
||||
|
||||
harness.intialize(directory);
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.stereotype.Controller;\n" +
|
||||
"import org.springframework.web.bind.annotation.DeleteMapping;\n" +
|
||||
"\n" +
|
||||
"@Controller\n" +
|
||||
"public class RestApi {\n" +
|
||||
"\n" +
|
||||
"@DeleteMapping(\"/greetings\")\n" +
|
||||
"public void deleteGreetings() {\n" +
|
||||
"}\n" +
|
||||
"\n" +
|
||||
"}",
|
||||
docUri);
|
||||
|
||||
editor.assertHoverContains("@DeleteMapping(\"/greetings\")", "[http://cfapps.io:999/greetings](http://cfapps.io:999/greetings)\n" +
|
||||
"\n" +
|
||||
"Process [PID=76543, name=`test-request-mapping-live-hover`]");
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testDeleteMappingHoverHintMethod2() throws Exception {
|
||||
|
||||
File directory = new File(
|
||||
ProjectsHarness.class.getResource("/test-projects/test-request-mapping-live-hover/").toURI());
|
||||
String docUri = directory.toPath().resolve("src/main/java/example/RestApi.java").toUri()
|
||||
.toString();
|
||||
|
||||
|
||||
// Build a mock running boot app
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.port("999")
|
||||
.processId("76543")
|
||||
.host("cfapps.io")
|
||||
.processName("test-request-mapping-live-hover")
|
||||
// Ugly, but this is real JSON copied from a real live running app. We want the
|
||||
// mock app to return realistic results if possible
|
||||
.requestMappings(
|
||||
"{\"{[/greetings],methods=[DELETE]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public void com.example.RestApi.deleteGreetings()\"}}")
|
||||
. build();
|
||||
|
||||
harness.intialize(directory);
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.stereotype.Controller;\n" +
|
||||
"import org.springframework.web.bind.annotation.DeleteMapping;\n" +
|
||||
"\n" +
|
||||
"@Controller\n" +
|
||||
"public class RestApi {\n" +
|
||||
"\n" +
|
||||
"@DeleteMapping(\"/greetings\")\n" +
|
||||
"public void deleteGreetings(int n) {\n" +
|
||||
"}\n" +
|
||||
"\n" +
|
||||
"}",
|
||||
docUri);
|
||||
|
||||
editor.assertNoHover("@DeleteMapping(\"/greetings\")");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoHoverHintMethod() throws Exception {
|
||||
|
||||
File directory = new File(
|
||||
ProjectsHarness.class.getResource("/test-projects/test-request-mapping-live-hover/").toURI());
|
||||
String docUri = directory.toPath().resolve("src/main/java/example/RestApi.java").toUri()
|
||||
.toString();
|
||||
|
||||
|
||||
// Build a mock running boot app
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.port("999")
|
||||
.processId("76543")
|
||||
.host("cfapps.io")
|
||||
.processName("test-request-mapping-live-hover")
|
||||
// Ugly, but this is real JSON copied from a real live running app. We want the
|
||||
// mock app to return realistic results if possible
|
||||
.requestMappings(
|
||||
"{\"{[/greetings],methods=[PUT]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public java.lang.String com.example.RestApi.updateGreetings()\"}}")
|
||||
. build();
|
||||
|
||||
harness.intialize(directory);
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"import java.lang.String;\n" +
|
||||
"import org.springframework.stereotype.Controller;\n" +
|
||||
"import org.springframework.web.bind.annotation.PutMapping;\n" +
|
||||
"\n" +
|
||||
"@Controller\n" +
|
||||
"public class RestApi {\n" +
|
||||
"\n" +
|
||||
"@PutMapping(\"/greetings\")\n" +
|
||||
"public String updateGreetings(int n) {\n" +
|
||||
"}\n" +
|
||||
"\n" +
|
||||
"}",
|
||||
docUri);
|
||||
|
||||
editor.assertNoHover("@PutMapping(\"/greetings\")");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiPathMappingHoverHintMethod1() throws Exception {
|
||||
|
||||
File directory = new File(
|
||||
ProjectsHarness.class.getResource("/test-projects/test-request-mapping-live-hover/").toURI());
|
||||
String docUri = directory.toPath().resolve("src/main/java/example/RestApi.java").toUri()
|
||||
.toString();
|
||||
|
||||
|
||||
// Build a mock running boot app
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.port("999")
|
||||
.processId("76543")
|
||||
.host("cfapps.io")
|
||||
.processName("test-request-mapping-live-hover")
|
||||
// Ugly, but this is real JSON copied from a real live running app. We want the
|
||||
// mock app to return realistic results if possible
|
||||
.requestMappings(
|
||||
"{\"{[/greetings || /hello],methods=[GET]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public java.lang.String com.example.RestApi.greetings()\"}}")
|
||||
. build();
|
||||
|
||||
harness.intialize(directory);
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.stereotype.Controller;\n" +
|
||||
"import org.springframework.web.bind.annotation.RequestMapping;\n" +
|
||||
"import org.springframework.web.bind.annotation.RequestMethod.*;\n" +
|
||||
"\n" +
|
||||
"@Controller\n" +
|
||||
"public class RestApi {\n" +
|
||||
"\n" +
|
||||
"@RequestMapping(value={\"/greetings\", \"/hello\"}, method=GET)\n" +
|
||||
"public String greetings() {\n" +
|
||||
"}\n" +
|
||||
"\n" +
|
||||
"}",
|
||||
docUri);
|
||||
|
||||
editor.assertHoverContains("@RequestMapping(value={\"/greetings\", \"/hello\"}, method=GET)", "[http://cfapps.io:999/greetings](http://cfapps.io:999/greetings) \n" +
|
||||
"[http://cfapps.io:999/hello](http://cfapps.io:999/hello)\n" +
|
||||
"\n" +
|
||||
"Process [PID=76543, name=`test-request-mapping-live-hover`]");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMethodMatchingHoverHintMethod1() throws Exception {
|
||||
|
||||
File directory = new File(
|
||||
ProjectsHarness.class.getResource("/test-projects/test-request-mapping-live-hover/").toURI());
|
||||
String docUri = directory.toPath().resolve("src/main/java/example/RestApi.java").toUri()
|
||||
.toString();
|
||||
|
||||
|
||||
// Build a mock running boot app
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.port("999")
|
||||
.processId("76543")
|
||||
.host("cfapps.io")
|
||||
.processName("test-request-mapping-live-hover")
|
||||
// Ugly, but this is real JSON copied from a real live running app. We want the
|
||||
// mock app to return realistic results if possible
|
||||
.requestMappings(
|
||||
"{\"{[/find],methods=[GET]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public org.springframework.http.ResponseEntity<?> com.example.RestApi.find(java.lang.String,java.util.Date,java.lang.String)\"}}")
|
||||
. build();
|
||||
|
||||
harness.intialize(directory);
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.http.ResponseEntity;\n" +
|
||||
"import java.lang.String;\n" +
|
||||
"import java.util.Date;\n" +
|
||||
"import org.springframework.stereotype.Controller;\n" +
|
||||
"import org.springframework.web.bind.annotation.RequestMapping;\n" +
|
||||
"import org.springframework.web.bind.annotation.RequestMethod.*;\n" +
|
||||
"\n" +
|
||||
"@Controller\n" +
|
||||
"public class RestApi {\n" +
|
||||
"\n" +
|
||||
"@RequestMapping(value=\"/find\", method=GET)\n" +
|
||||
"public ResponseEntity<?> find(String p1, Date p2, String p3) {\n" +
|
||||
"return null;\n" +
|
||||
"}\n" +
|
||||
"\n" +
|
||||
"}",
|
||||
docUri);
|
||||
|
||||
editor.assertHoverContains("@RequestMapping(value=\"/find\", method=GET)", "[http://cfapps.io:999/find](http://cfapps.io:999/find)\n" +
|
||||
"\n" +
|
||||
"Process [PID=76543, name=`test-request-mapping-live-hover`]");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMethodMatchingHoverHintMethod2() throws Exception {
|
||||
|
||||
File directory = new File(
|
||||
ProjectsHarness.class.getResource("/test-projects/test-request-mapping-live-hover/").toURI());
|
||||
String docUri = directory.toPath().resolve("src/main/java/example/RestApi.java").toUri()
|
||||
.toString();
|
||||
|
||||
|
||||
// Build a mock running boot app
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.port("999")
|
||||
.processId("76543")
|
||||
.host("cfapps.io")
|
||||
.processName("test-request-mapping-live-hover")
|
||||
// Ugly, but this is real JSON copied from a real live running app. We want the
|
||||
// mock app to return realistic results if possible
|
||||
.requestMappings(
|
||||
"{\"{[/find],methods=[GET]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public java.lang.Object com.example.RestApi.set(java.lang.String,java.util.Map<java.lang.String, java.lang.String>)\"}}")
|
||||
. build();
|
||||
|
||||
harness.intialize(directory);
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.http.ResponseEntity;\n" +
|
||||
"import java.lang.String;\n" +
|
||||
"import java.util.Map;\n" +
|
||||
"import org.springframework.stereotype.Controller;\n" +
|
||||
"import org.springframework.web.bind.annotation.RequestMapping;\n" +
|
||||
"import org.springframework.web.bind.annotation.RequestMethod.*;\n" +
|
||||
"\n" +
|
||||
"@Controller\n" +
|
||||
"public class RestApi {\n" +
|
||||
"\n" +
|
||||
"@RequestMapping(value=\"/find\", method=GET)\n" +
|
||||
"public Object set(String p1, Map<String, String> p2) {\n" +
|
||||
"return null;\n" +
|
||||
"}\n" +
|
||||
"\n" +
|
||||
"}",
|
||||
docUri);
|
||||
|
||||
editor.assertHoverContains("@RequestMapping(value=\"/find\", method=GET)", "[http://cfapps.io:999/find](http://cfapps.io:999/find)\n" +
|
||||
"\n" +
|
||||
"Process [PID=76543, name=`test-request-mapping-live-hover`]");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMethodMatchingHoverHintMethod3() throws Exception {
|
||||
|
||||
File directory = new File(
|
||||
ProjectsHarness.class.getResource("/test-projects/test-request-mapping-live-hover/").toURI());
|
||||
String docUri = directory.toPath().resolve("src/main/java/example/RestApi.java").toUri()
|
||||
.toString();
|
||||
|
||||
|
||||
// Build a mock running boot app
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.port("999")
|
||||
.processId("76543")
|
||||
.host("cfapps.io")
|
||||
.processName("test-request-mapping-live-hover")
|
||||
// Ugly, but this is real JSON copied from a real live running app. We want the
|
||||
// mock app to return realistic results if possible
|
||||
.requestMappings(
|
||||
"{\"{[/find],methods=[GET]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public java.lang.Object com.example.RestApi.set(java.lang.String,java.util.Map<java.lang.String, java.util.Map<java.lang.String, java.lang.Integer>>)\"}}")
|
||||
. build();
|
||||
|
||||
harness.intialize(directory);
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.http.ResponseEntity;\n" +
|
||||
"import java.lang.String;\n" +
|
||||
"import java.lang.Integer;\n" +
|
||||
"import java.util.Map;\n" +
|
||||
"import org.springframework.stereotype.Controller;\n" +
|
||||
"import org.springframework.web.bind.annotation.RequestMapping;\n" +
|
||||
"import org.springframework.web.bind.annotation.RequestMethod.*;\n" +
|
||||
"\n" +
|
||||
"@Controller\n" +
|
||||
"public class RestApi {\n" +
|
||||
"\n" +
|
||||
"@RequestMapping(value=\"/find\", method=GET)\n" +
|
||||
"public Object set(String p1, Map<String, Map<String, Integer>> p2) {\n" +
|
||||
"return null;\n" +
|
||||
"}\n" +
|
||||
"\n" +
|
||||
"}",
|
||||
docUri);
|
||||
|
||||
editor.assertHoverContains("@RequestMapping(value=\"/find\", method=GET)", "[http://cfapps.io:999/find](http://cfapps.io:999/find)\n" +
|
||||
"\n" +
|
||||
"Process [PID=76543, name=`test-request-mapping-live-hover`]");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWildCardMethodMatchingHoverHint() throws Exception {
|
||||
|
||||
File directory = new File(
|
||||
ProjectsHarness.class.getResource("/test-projects/test-request-mapping-live-hover/").toURI());
|
||||
String docUri = directory.toPath().resolve("src/main/java/example/RestApi.java").toUri()
|
||||
.toString();
|
||||
|
||||
|
||||
// Build a mock running boot app
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.port("999")
|
||||
.processId("76543")
|
||||
.host("cfapps.io")
|
||||
.processName("test-request-mapping-live-hover")
|
||||
// Ugly, but this is real JSON copied from a real live running app. We want the
|
||||
// mock app to return realistic results if possible
|
||||
.requestMappings(
|
||||
"{\"{[/find],methods=[GET]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public java.lang.Object com.example.RestApi.set(java.lang.String,java.util.Map<java.lang.String, java.util.Map<java.lang.String, ? extends java.lang.Integer>>)\"}}")
|
||||
. build();
|
||||
|
||||
harness.intialize(directory);
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.http.ResponseEntity;\n" +
|
||||
"import java.lang.String;\n" +
|
||||
"import java.lang.Integer;\n" +
|
||||
"import java.util.Map;\n" +
|
||||
"import org.springframework.stereotype.Controller;\n" +
|
||||
"import org.springframework.web.bind.annotation.RequestMapping;\n" +
|
||||
"import org.springframework.web.bind.annotation.RequestMethod.*;\n" +
|
||||
"\n" +
|
||||
"@Controller\n" +
|
||||
"public class RestApi {\n" +
|
||||
"\n" +
|
||||
"@RequestMapping(value=\"/find\", method=GET)\n" +
|
||||
"public Object set(String p1, Map<String, Map<String, ? extends Integer>> p2) {\n" +
|
||||
"return null;\n" +
|
||||
"}\n" +
|
||||
"\n" +
|
||||
"}",
|
||||
docUri);
|
||||
|
||||
editor.assertHoverContains("@RequestMapping(value=\"/find\", method=GET)", "[http://cfapps.io:999/find](http://cfapps.io:999/find)\n" +
|
||||
"\n" +
|
||||
"Process [PID=76543, name=`test-request-mapping-live-hover`]");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testArrayMethodMatchingHoverHint() throws Exception {
|
||||
|
||||
File directory = new File(
|
||||
ProjectsHarness.class.getResource("/test-projects/test-request-mapping-live-hover/").toURI());
|
||||
String docUri = directory.toPath().resolve("src/main/java/example/RestApi.java").toUri()
|
||||
.toString();
|
||||
|
||||
|
||||
// Build a mock running boot app
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.port("999")
|
||||
.processId("76543")
|
||||
.host("cfapps.io")
|
||||
.processName("test-request-mapping-live-hover")
|
||||
// Ugly, but this is real JSON copied from a real live running app. We want the
|
||||
// mock app to return realistic results if possible
|
||||
.requestMappings(
|
||||
"{\"{[/find],methods=[GET]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public java.lang.Object com.example.RestApi.set(java.lang.String[])\"}}")
|
||||
. build();
|
||||
|
||||
harness.intialize(directory);
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.http.ResponseEntity;\n" +
|
||||
"import java.lang.String;\n" +
|
||||
"import org.springframework.stereotype.Controller;\n" +
|
||||
"import org.springframework.web.bind.annotation.RequestMapping;\n" +
|
||||
"import org.springframework.web.bind.annotation.RequestMethod.*;\n" +
|
||||
"\n" +
|
||||
"@Controller\n" +
|
||||
"public class RestApi {\n" +
|
||||
"\n" +
|
||||
"@RequestMapping(value=\"/find\", method=GET)\n" +
|
||||
"public Object set(String[] p) {\n" +
|
||||
"return null;\n" +
|
||||
"}\n" +
|
||||
"\n" +
|
||||
"}",
|
||||
docUri);
|
||||
|
||||
editor.assertHoverContains("@RequestMapping(value=\"/find\", method=GET)", "[http://cfapps.io:999/find](http://cfapps.io:999/find)\n" +
|
||||
"\n" +
|
||||
"Process [PID=76543, name=`test-request-mapping-live-hover`]");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiDimensionalArrayMethodMatchingHoverHint() throws Exception {
|
||||
|
||||
File directory = new File(
|
||||
ProjectsHarness.class.getResource("/test-projects/test-request-mapping-live-hover/").toURI());
|
||||
String docUri = directory.toPath().resolve("src/main/java/example/RestApi.java").toUri()
|
||||
.toString();
|
||||
|
||||
|
||||
// Build a mock running boot app
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.port("999")
|
||||
.processId("76543")
|
||||
.host("cfapps.io")
|
||||
.processName("test-request-mapping-live-hover")
|
||||
// Ugly, but this is real JSON copied from a real live running app. We want the
|
||||
// mock app to return realistic results if possible
|
||||
.requestMappings(
|
||||
"{\"{[/find],methods=[GET]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public java.lang.Object com.example.RestApi.set(java.lang.String[][])\"}}")
|
||||
. build();
|
||||
|
||||
harness.intialize(directory);
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.http.ResponseEntity;\n" +
|
||||
"import java.lang.String;\n" +
|
||||
"import org.springframework.stereotype.Controller;\n" +
|
||||
"import org.springframework.web.bind.annotation.RequestMapping;\n" +
|
||||
"import org.springframework.web.bind.annotation.RequestMethod.*;\n" +
|
||||
"\n" +
|
||||
"@Controller\n" +
|
||||
"public class RestApi {\n" +
|
||||
"\n" +
|
||||
"@RequestMapping(value=\"/find\", method=GET)\n" +
|
||||
"public Object set(String[][] p) {\n" +
|
||||
"return null;\n" +
|
||||
"}\n" +
|
||||
"\n" +
|
||||
"}",
|
||||
docUri);
|
||||
|
||||
editor.assertHoverContains("@RequestMapping(value=\"/find\", method=GET)", "[http://cfapps.io:999/find](http://cfapps.io:999/find)\n" +
|
||||
"\n" +
|
||||
"Process [PID=76543, name=`test-request-mapping-live-hover`]");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVarArgsMethodMatchingHoverHint() throws Exception {
|
||||
|
||||
File directory = new File(
|
||||
ProjectsHarness.class.getResource("/test-projects/test-request-mapping-live-hover/").toURI());
|
||||
String docUri = directory.toPath().resolve("src/main/java/example/RestApi.java").toUri()
|
||||
.toString();
|
||||
|
||||
|
||||
// Build a mock running boot app
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.port("999")
|
||||
.processId("76543")
|
||||
.host("cfapps.io")
|
||||
.processName("test-request-mapping-live-hover")
|
||||
// Ugly, but this is real JSON copied from a real live running app. We want the
|
||||
// mock app to return realistic results if possible
|
||||
.requestMappings(
|
||||
"{\"{[/find],methods=[GET]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public java.lang.Object com.example.RestApi.set(java.lang.String...)\"}}")
|
||||
. build();
|
||||
|
||||
harness.intialize(directory);
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.http.ResponseEntity;\n" +
|
||||
"import java.lang.String;\n" +
|
||||
"import org.springframework.stereotype.Controller;\n" +
|
||||
"import org.springframework.web.bind.annotation.RequestMapping;\n" +
|
||||
"import org.springframework.web.bind.annotation.RequestMethod.*;\n" +
|
||||
"\n" +
|
||||
"@Controller\n" +
|
||||
"public class RestApi {\n" +
|
||||
"\n" +
|
||||
"@RequestMapping(value=\"/find\", method=GET)\n" +
|
||||
"public Object set(String... p) {\n" +
|
||||
"return null;\n" +
|
||||
"}\n" +
|
||||
"\n" +
|
||||
"}",
|
||||
docUri);
|
||||
|
||||
editor.assertHoverContains("@RequestMapping(value=\"/find\", method=GET)", "[http://cfapps.io:999/find](http://cfapps.io:999/find)\n" +
|
||||
"\n" +
|
||||
"Process [PID=76543, name=`test-request-mapping-live-hover`]");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleAppsLiveHover() throws Exception {
|
||||
|
||||
File directory = new File(
|
||||
ProjectsHarness.class.getResource("/test-projects/test-request-mapping-live-hover/").toURI());
|
||||
String docUri = directory.toPath().resolve("src/main/java/example/RestApi.java").toUri()
|
||||
.toString();
|
||||
|
||||
// Build three different instances of the same app running on different ports with different process IDs
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.port("1000")
|
||||
.processId("70000")
|
||||
.host("cfapps.io")
|
||||
.processName("test-request-mapping-live-hover")
|
||||
// Ugly, but this is real JSON copied from a real live running app. We want the
|
||||
// mock app to return realistic results if possible
|
||||
.requestMappings(
|
||||
"{\"/webjars/**\":{\"bean\":\"resourceHandlerMapping\"},\"/**\":{\"bean\":\"resourceHandlerMapping\"},\"/**/favicon.ico\":{\"bean\":\"faviconHandlerMapping\"},\"{[/hello-world],methods=[GET]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public example.Greeting example.HelloWorldController.sayHello(java.lang.String)\"},\"{[/goodbye]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public java.lang.String example.RestApi.goodbye()\"},\"{[/hello]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public java.lang.String example.RestApi.hello()\"},\"{[/error]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)\"},\"{[/error],produces=[text/html]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)\"}}")
|
||||
. build();
|
||||
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.port("1001")
|
||||
.processId("80000")
|
||||
.host("cfapps.io")
|
||||
.processName("test-request-mapping-live-hover")
|
||||
// Ugly, but this is real JSON copied from a real live running app. We want the
|
||||
// mock app to return realistic results if possible
|
||||
.requestMappings(
|
||||
"{\"/webjars/**\":{\"bean\":\"resourceHandlerMapping\"},\"/**\":{\"bean\":\"resourceHandlerMapping\"},\"/**/favicon.ico\":{\"bean\":\"faviconHandlerMapping\"},\"{[/hello-world],methods=[GET]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public example.Greeting example.HelloWorldController.sayHello(java.lang.String)\"},\"{[/goodbye]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public java.lang.String example.RestApi.goodbye()\"},\"{[/hello]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public java.lang.String example.RestApi.hello()\"},\"{[/error]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)\"},\"{[/error],produces=[text/html]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)\"}}")
|
||||
. build();
|
||||
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.port("1002")
|
||||
.processId("90000")
|
||||
.host("cfapps.io")
|
||||
.processName("test-request-mapping-live-hover")
|
||||
// Ugly, but this is real JSON copied from a real live running app. We want the
|
||||
// mock app to return realistic results if possible
|
||||
.requestMappings(
|
||||
"{\"/webjars/**\":{\"bean\":\"resourceHandlerMapping\"},\"/**\":{\"bean\":\"resourceHandlerMapping\"},\"/**/favicon.ico\":{\"bean\":\"faviconHandlerMapping\"},\"{[/hello-world],methods=[GET]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public example.Greeting example.HelloWorldController.sayHello(java.lang.String)\"},\"{[/goodbye]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public java.lang.String example.RestApi.goodbye()\"},\"{[/hello]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public java.lang.String example.RestApi.hello()\"},\"{[/error]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)\"},\"{[/error],produces=[text/html]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)\"}}")
|
||||
. build();
|
||||
harness.intialize(directory);
|
||||
|
||||
Editor editor = harness.newEditorFromFileUri(docUri, LanguageId.JAVA);
|
||||
editor.assertHoverContains("@RequestMapping(\"/hello\")", "[http://cfapps.io:1000/hello](http://cfapps.io:1000/hello)\n" +
|
||||
"\n" +
|
||||
"Process [PID=70000, name=`test-request-mapping-live-hover`]\n" +
|
||||
"\n" +
|
||||
"---\n" +
|
||||
"\n" +
|
||||
"[http://cfapps.io:1001/hello](http://cfapps.io:1001/hello)\n" +
|
||||
"\n" +
|
||||
"Process [PID=80000, name=`test-request-mapping-live-hover`]\n" +
|
||||
"\n" +
|
||||
"---\n" +
|
||||
"\n" +
|
||||
"[http://cfapps.io:1002/hello](http://cfapps.io:1002/hello)\n" +
|
||||
"\n" +
|
||||
"Process [PID=90000, name=`test-request-mapping-live-hover`]");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHighlights() throws Exception {
|
||||
|
||||
File directory = new File(
|
||||
ProjectsHarness.class.getResource("/test-projects/test-request-mapping-live-hover/").toURI());
|
||||
String docUri = directory.toPath().resolve("src/main/java/example/RestApi.java").toUri()
|
||||
.toString();
|
||||
|
||||
|
||||
// Build a mock running boot app
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.port("999")
|
||||
.processId("76543")
|
||||
.host("cfapps.io")
|
||||
.processName("test-request-mapping-live-hover")
|
||||
// Ugly, but this is real JSON copied from a real live running app. We want the
|
||||
// mock app to return realistic results if possible
|
||||
.requestMappings(
|
||||
"{\"/webjars/**\":{\"bean\":\"resourceHandlerMapping\"},\"/**\":{\"bean\":\"resourceHandlerMapping\"},\"/**/favicon.ico\":{\"bean\":\"faviconHandlerMapping\"},\"{[/goodbye]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public java.lang.String example.RestApi.goodbye()\"},\"{[/delete/{id}],methods=[DELETE]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public java.lang.String example.RestApi.removeMe(int)\"},\"{[/hello]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public java.lang.String example.RestApi.hello()\"},\"{[/postHello],methods=[POST]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public java.lang.String example.RestApi.postMethod(java.lang.String)\"},\"{[/put/{id}],methods=[PUT]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public java.lang.String example.RestApi.putMethod(int,java.lang.String)\"},\"{[/person/{name}],methods=[GET]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public java.lang.String example.RestApi.getMapping(java.lang.String)\"},\"{[/error]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)\"},\"{[/error],produces=[text/html]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)\"},\"{[/application/status],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}\":{\"bean\":\"webEndpointServletHandlerMapping\",\"method\":\"public java.lang.Object org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping$OperationHandler.handle(javax.servlet.http.HttpServletRequest,java.util.Map<java.lang.String, java.lang.String>)\"},\"{[/application/info],methods=[GET],produces=[application/vnd.spring-boot.actuator.v2+json || application/json]}\":{\"bean\":\"webEndpointServletHandlerMapping\",\"method\":\"public java.lang.Object org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping$OperationHandler.handle(javax.servlet.http.HttpServletRequest,java.util.Map<java.lang.String, java.lang.String>)\"},\"{[/application],methods=[GET]}\":{\"bean\":\"webEndpointServletHandlerMapping\",\"method\":\"private java.util.Map<java.lang.String, java.util.Map<java.lang.String, org.springframework.boot.actuate.endpoint.web.Link>> org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping.links(javax.servlet.http.HttpServletRequest)\"}}")
|
||||
|
||||
.build();
|
||||
|
||||
harness.intialize(directory);
|
||||
|
||||
Editor editor = harness.newEditorFromFileUri(docUri, LanguageId.JAVA);
|
||||
|
||||
editor.assertHighlights("@RequestMapping(\"/hello\")", "@RequestMapping(\"/goodbye\")",
|
||||
"@GetMapping(\"/person/{name}\")", "@DeleteMapping(\"/delete/{id}\")", "@PostMapping(\"/postHello\")",
|
||||
"@PutMapping(\"/put/{id}\")");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMappingHoversInInnerClasses() throws Exception {
|
||||
|
||||
File directory = new File(
|
||||
ProjectsHarness.class.getResource("/test-projects/test-request-mapping-live-hover/").toURI());
|
||||
String docUri = directory.toPath().resolve("src/main/java/example/InnerClassController.java").toUri()
|
||||
.toString();
|
||||
|
||||
|
||||
// Build a mock running boot app
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.port("1111")
|
||||
.processId("22022")
|
||||
.host("cfapps.io")
|
||||
.processName("test-request-mapping-live-hover")
|
||||
// Ugly, but this is real JSON copied from a real live running app. We want the
|
||||
// mock app to return realistic results if possible
|
||||
.requestMappings(
|
||||
"{\"/webjars/**\":{\"bean\":\"resourceHandlerMapping\"},\"/**\":{\"bean\":\"resourceHandlerMapping\"},\"/**/favicon.ico\":{\"bean\":\"faviconHandlerMapping\"},\"{[/hello-world],methods=[GET]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public example.Greeting example.HelloWorldController.sayHello(java.lang.String)\"},\"{[/inner-inner-class]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public java.lang.String example.InnerClassController$InnerController$InnerInnerController.saySomethingSuperInnerClass()\"},\"{[/inner-class]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public java.lang.String example.InnerClassController$InnerController.saySomething()\"},\"{[/person/{name}],methods=[GET]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public java.lang.String example.RestApi.getMapping(java.lang.String)\"},\"{[/delete/{id}],methods=[DELETE]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public java.lang.String example.RestApi.removeMe(int)\"},\"{[/goodbye]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public java.lang.String example.RestApi.goodbye()\"},\"{[/hello]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public java.lang.String example.RestApi.hello()\"},\"{[/postHello],methods=[POST]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public java.lang.String example.RestApi.postMethod(java.lang.String)\"},\"{[/put/{id}],methods=[PUT]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public java.lang.String example.RestApi.putMethod(int,java.lang.String)\"},\"{[/error]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)\"},\"{[/error],produces=[text/html]}\":{\"bean\":\"requestMappingHandlerMapping\",\"method\":\"public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)\"}}")
|
||||
.build();
|
||||
|
||||
harness.intialize(directory);
|
||||
|
||||
Editor editor = harness.newEditorFromFileUri(docUri, LanguageId.JAVA);
|
||||
editor.assertHighlights("@RequestMapping(\"/inner-class\")", "@RequestMapping(\"/inner-inner-class\")");
|
||||
|
||||
editor.assertHoverContains("@RequestMapping(\"/inner-class\")", "[http://cfapps.io:1111/inner-class](http://cfapps.io:1111/inner-class)\n" +
|
||||
"\n" +
|
||||
"Process [PID=22022, name=`test-request-mapping-live-hover`]");
|
||||
|
||||
editor.assertHoverContains("@RequestMapping(\"/inner-inner-class\")", "[http://cfapps.io:1111/inner-inner-class](http://cfapps.io:1111/inner-inner-class)\n" +
|
||||
"\n" +
|
||||
"Process [PID=22022, name=`test-request-mapping-live-hover`]");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,181 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2018 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.requestmapping.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.lsp4j.SymbolInformation;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class RequestMappingSymbolProviderTest {
|
||||
|
||||
private BootLanguageServerHarness harness;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
harness = BootLanguageServerHarness.builder().build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleRequestMappingSymbol() throws Exception {
|
||||
harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/test-request-mapping-symbols/").toURI()));
|
||||
|
||||
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-request-mapping-symbols/").toURI());
|
||||
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/SimpleMappingClass.java").toUri().toString();
|
||||
List<? extends SymbolInformation> symbols = harness.getServerWrapper().getSpringIndexer().getSymbols(docUri);
|
||||
assertEquals(1, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/greeting", docUri, 6, 1, 6, 29));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParentRequestMappingSymbol() throws Exception {
|
||||
harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/test-request-mapping-symbols/").toURI()));
|
||||
|
||||
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-request-mapping-symbols/").toURI());
|
||||
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/ParentMappingClass.java").toUri().toString();
|
||||
List<? extends SymbolInformation> symbols = harness.getServerWrapper().getSpringIndexer().getSymbols(docUri);
|
||||
assertEquals(1, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/parent/greeting -- GET", docUri, 8, 1, 8, 47));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyPathWithParentRequestMappingSymbol() throws Exception {
|
||||
harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/test-request-mapping-symbols/").toURI()));
|
||||
|
||||
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-request-mapping-symbols/").toURI());
|
||||
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/ParentMappingClass2.java").toUri().toString();
|
||||
List<? extends SymbolInformation> symbols = harness.getServerWrapper().getSpringIndexer().getSymbols(docUri);
|
||||
assertEquals(1, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/parent2 -- GET,POST,DELETE", docUri, 8, 1, 8, 16));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiRequestMappingSymbol() throws Exception {
|
||||
harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/test-request-mapping-symbols/").toURI()));
|
||||
|
||||
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-request-mapping-symbols/").toURI());
|
||||
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/MultiRequestMappingClass.java").toUri().toString();
|
||||
List<? extends SymbolInformation> symbols = harness.getServerWrapper().getSpringIndexer().getSymbols(docUri);
|
||||
assertEquals(2, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/hello1", docUri, 6, 1, 6, 44));
|
||||
assertTrue(containsSymbol(symbols, "@/hello2", docUri, 6, 1, 6, 44));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMappingSymbol() throws Exception {
|
||||
harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/test-request-mapping-symbols/").toURI()));
|
||||
|
||||
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-request-mapping-symbols/").toURI());
|
||||
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/RequestMethodClass.java").toUri().toString();
|
||||
List<? extends SymbolInformation> symbols = harness.getServerWrapper().getSpringIndexer().getSymbols(docUri);
|
||||
assertTrue(containsSymbol(symbols, "@/getData -- GET", docUri, 12, 1, 12, 24));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteMappingSymbol() throws Exception {
|
||||
harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/test-request-mapping-symbols/").toURI()));
|
||||
|
||||
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-request-mapping-symbols/").toURI());
|
||||
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/RequestMethodClass.java").toUri().toString();
|
||||
List<? extends SymbolInformation> symbols = harness.getServerWrapper().getSpringIndexer().getSymbols(docUri);
|
||||
assertTrue(containsSymbol(symbols, "@/deleteData -- DELETE",docUri, 20, 1, 20, 30));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPostMappingSymbol() throws Exception {
|
||||
harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/test-request-mapping-symbols/").toURI()));
|
||||
|
||||
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-request-mapping-symbols/").toURI());
|
||||
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/RequestMethodClass.java").toUri().toString();
|
||||
List<? extends SymbolInformation> symbols = harness.getServerWrapper().getSpringIndexer().getSymbols(docUri);
|
||||
assertTrue(containsSymbol(symbols, "@/postData -- POST", docUri, 24, 1, 24, 26));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutMappingSymbol() throws Exception {
|
||||
harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/test-request-mapping-symbols/").toURI()));
|
||||
|
||||
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-request-mapping-symbols/").toURI());
|
||||
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/RequestMethodClass.java").toUri().toString();
|
||||
List<? extends SymbolInformation> symbols = harness.getServerWrapper().getSpringIndexer().getSymbols(docUri);
|
||||
assertTrue(containsSymbol(symbols, "@/putData -- PUT", docUri, 16, 1, 16, 24));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPatchMappingSymbol() throws Exception {
|
||||
harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/test-request-mapping-symbols/").toURI()));
|
||||
|
||||
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-request-mapping-symbols/").toURI());
|
||||
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/RequestMethodClass.java").toUri().toString();
|
||||
List<? extends SymbolInformation> symbols = harness.getServerWrapper().getSpringIndexer().getSymbols(docUri);
|
||||
assertTrue(containsSymbol(symbols, "@/patchData -- PATCH", docUri, 28, 1, 28, 28));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRequestMappingSymbol() throws Exception {
|
||||
harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/test-request-mapping-symbols/").toURI()));
|
||||
|
||||
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-request-mapping-symbols/").toURI());
|
||||
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/RequestMethodClass.java").toUri().toString();
|
||||
List<? extends SymbolInformation> symbols = harness.getServerWrapper().getSpringIndexer().getSymbols(docUri);
|
||||
assertTrue(containsSymbol(symbols, "@/getHello -- GET", docUri, 32, 1, 32, 61));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiRequestMethodMappingSymbol() throws Exception {
|
||||
harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/test-request-mapping-symbols/").toURI()));
|
||||
|
||||
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-request-mapping-symbols/").toURI());
|
||||
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/RequestMethodClass.java").toUri().toString();
|
||||
List<? extends SymbolInformation> symbols = harness.getServerWrapper().getSpringIndexer().getSymbols(docUri);
|
||||
assertTrue(containsSymbol(symbols, "@/postAndPutHello -- POST,PUT", docUri, 36, 1, 36, 76));
|
||||
}
|
||||
|
||||
private boolean containsSymbol(List<? extends SymbolInformation> symbols, String name, String uri, int startLine, int startCHaracter, int endLine, int endCharacter) {
|
||||
for (Iterator<? extends SymbolInformation> iterator = symbols.iterator(); iterator.hasNext();) {
|
||||
SymbolInformation symbol = iterator.next();
|
||||
|
||||
if (symbol.getName().equals(name)
|
||||
&& symbol.getLocation().getUri().equals(uri)
|
||||
&& symbol.getLocation().getRange().getStart().getLine() == startLine
|
||||
&& symbol.getLocation().getRange().getStart().getCharacter() == startCHaracter
|
||||
&& symbol.getLocation().getRange().getEnd().getLine() == endLine
|
||||
&& symbol.getLocation().getRange().getEnd().getCharacter() == endCharacter) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.scope.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.eclipse.lsp4j.CompletionItem;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.Editor;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.TestAsserts;
|
||||
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class ScopeCompletionTest {
|
||||
|
||||
private BootLanguageServerHarness harness;
|
||||
private Editor editor;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
IJavaProject testProject = ProjectsHarness.INSTANCE.mavenProject("test-annotations");
|
||||
harness = BootLanguageServerHarness.builder().mockDefaults().build();
|
||||
harness.useProject(testProject);
|
||||
harness.intialize(null);
|
||||
}
|
||||
|
||||
// private IJavaProject getTestProject() {
|
||||
// return testProject;
|
||||
// }
|
||||
|
||||
@Test
|
||||
public void testEmptyBracketsCompletion() throws Exception {
|
||||
prepareCase("@Scope(\"onClass\")", "@Scope(<*>)");
|
||||
assertAnnotationCompletions(
|
||||
"@Scope(\"application\"<*>)",
|
||||
"@Scope(\"globalSession\"<*>)",
|
||||
"@Scope(\"prototype\"<*>)",
|
||||
"@Scope(\"request\"<*>)",
|
||||
"@Scope(\"session\"<*>)",
|
||||
"@Scope(\"singleton\"<*>)",
|
||||
"@Scope(\"websocket\"<*>)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyStringLiteralCompletion() throws Exception {
|
||||
prepareCase("@Scope(\"onClass\")", "@Scope(\"<*>\")");
|
||||
assertAnnotationCompletions(
|
||||
"@Scope(\"application\"<*>)",
|
||||
"@Scope(\"globalSession\"<*>)",
|
||||
"@Scope(\"prototype\"<*>)",
|
||||
"@Scope(\"request\"<*>)",
|
||||
"@Scope(\"session\"<*>)",
|
||||
"@Scope(\"singleton\"<*>)",
|
||||
"@Scope(\"websocket\"<*>)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyValueCompletion() throws Exception {
|
||||
prepareCase("@Scope(\"onClass\")", "@Scope(value=<*>)");
|
||||
assertAnnotationCompletions(
|
||||
"@Scope(value=\"application\"<*>)",
|
||||
"@Scope(value=\"globalSession\"<*>)",
|
||||
"@Scope(value=\"prototype\"<*>)",
|
||||
"@Scope(value=\"request\"<*>)",
|
||||
"@Scope(value=\"session\"<*>)",
|
||||
"@Scope(value=\"singleton\"<*>)",
|
||||
"@Scope(value=\"websocket\"<*>)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyValueStringLiteralCompletion() throws Exception {
|
||||
prepareCase("@Scope(\"onClass\")", "@Scope(value=\"<*>\")");
|
||||
assertAnnotationCompletions(
|
||||
"@Scope(value=\"application\"<*>)",
|
||||
"@Scope(value=\"globalSession\"<*>)",
|
||||
"@Scope(value=\"prototype\"<*>)",
|
||||
"@Scope(value=\"request\"<*>)",
|
||||
"@Scope(value=\"session\"<*>)",
|
||||
"@Scope(value=\"singleton\"<*>)",
|
||||
"@Scope(value=\"websocket\"<*>)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrefixWithClosingQuotesCompletion() throws Exception {
|
||||
prepareCase("@Scope(\"onClass\")", "@Scope(\"pro<*>\")");
|
||||
assertAnnotationCompletions(
|
||||
"@Scope(\"prototype\"<*>)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrefixWithoutClosingQuotesCompletion() throws Exception {
|
||||
prepareCase("@Scope(\"onClass\")", "@Scope(\"pro<*>)");
|
||||
assertAnnotationCompletions();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValuePrefixWithClosingQuotesCompletion() throws Exception {
|
||||
prepareCase("@Scope(\"onClass\")", "@Scope(value=\"pro<*>\")");
|
||||
assertAnnotationCompletions(
|
||||
"@Scope(value=\"prototype\"<*>)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValuePrefixWithoutClosingQuotesCompletion() throws Exception {
|
||||
prepareCase("@Scope(\"onClass\")", "@Scope(value=\"pro<*>)");
|
||||
assertAnnotationCompletions();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrefixReplaceRestCompletion() throws Exception {
|
||||
prepareCase("@Scope(\"onClass\")", "@Scope(\"pro<*>something\")");
|
||||
assertAnnotationCompletions(
|
||||
"@Scope(\"prototype\"<*>)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDifferentMemberNameCompletion() throws Exception {
|
||||
prepareCase("@Scope(\"onClass\")", "@Scope(proxyName=\"<*>\")");
|
||||
assertAnnotationCompletions();
|
||||
}
|
||||
|
||||
private void prepareCase(String selectedAnnotation, String annotationStatementBeforeTest) throws Exception {
|
||||
InputStream resource = this.getClass().getResourceAsStream("/test-projects/test-annotations/src/main/java/org/test/TestScopeCompletion.java");
|
||||
String content = IOUtils.toString(resource);
|
||||
|
||||
content = content.replace(selectedAnnotation, annotationStatementBeforeTest);
|
||||
editor = new Editor(harness, content, LanguageId.JAVA);
|
||||
}
|
||||
|
||||
private void assertAnnotationCompletions(String... completedAnnotations) throws Exception {
|
||||
List<CompletionItem> completions = editor.getCompletions();
|
||||
int i = 0;
|
||||
for (String expectedCompleted : completedAnnotations) {
|
||||
Editor clonedEditor = editor.clone();
|
||||
clonedEditor.apply(completions.get(i++));
|
||||
TestAsserts.assertContains(expectedCompleted, clonedEditor.getText());
|
||||
}
|
||||
|
||||
assertEquals(i, completions.size());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,203 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2018 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.utils.test;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import org.eclipse.jdt.core.dom.CompilationUnit;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.ide.vscode.commons.java.DelegatingCachedClasspath;
|
||||
import org.springframework.ide.vscode.commons.java.IClasspath;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.maven.MavenCore;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
|
||||
|
||||
/**
|
||||
* CU Cache tests
|
||||
*
|
||||
* @author Alex Boyko
|
||||
*
|
||||
*/
|
||||
public class CompilationUnitCacheTest {
|
||||
|
||||
private BootLanguageServerHarness harness;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
harness = BootLanguageServerHarness.builder().build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cu_cached() throws Exception {
|
||||
harness = BootLanguageServerHarness.builder()
|
||||
.mockDefaults().build();
|
||||
harness.useProject(new IJavaProject() {
|
||||
@Override
|
||||
public IClasspath getClasspath() {
|
||||
return new DelegatingCachedClasspath<>(() -> null, null);
|
||||
}
|
||||
});
|
||||
harness.intialize(null);
|
||||
|
||||
TextDocument doc = new TextDocument(harness.createTempUri(), LanguageId.JAVA, 0, "package my.package\n" +
|
||||
"\n" +
|
||||
"public class SomeClass {\n" +
|
||||
"\n" +
|
||||
"}\n");
|
||||
CompilationUnit cu = getCompilationUnit(doc);
|
||||
assertNotNull(cu);
|
||||
|
||||
CompilationUnit cuAnother = getCompilationUnit(doc);
|
||||
assertTrue(cu == cuAnother);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cu_not_generated_without_project() throws Exception {
|
||||
harness.intialize(null);
|
||||
|
||||
TextDocument doc = new TextDocument(harness.createTempUri(), LanguageId.JAVA, 0, "package my.package\n" +
|
||||
"\n" +
|
||||
"public class SomeClass {\n" +
|
||||
"\n" +
|
||||
"}\n");
|
||||
CompilationUnit cu = getCompilationUnit(doc);
|
||||
assertNull(cu);
|
||||
}
|
||||
|
||||
private CompilationUnit getCompilationUnit(TextDocument doc) {
|
||||
return harness.getServerWrapper().getCompilationUnitCache().withCompilationUnit(doc, cu -> cu);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cu_cache_invalidated_by_doc_change() throws Exception {
|
||||
harness = BootLanguageServerHarness.builder()
|
||||
.mockDefaults().build();
|
||||
harness.useProject(new IJavaProject() {
|
||||
@Override
|
||||
public IClasspath getClasspath() {
|
||||
return new DelegatingCachedClasspath<>(() -> null, null);
|
||||
}
|
||||
});
|
||||
harness.intialize(null);
|
||||
|
||||
TextDocument doc = new TextDocument(harness.createTempUri(), LanguageId.JAVA, 0, "package my.package\n" +
|
||||
"\n" +
|
||||
"public class SomeClass {\n" +
|
||||
"\n" +
|
||||
"}\n");
|
||||
|
||||
harness.newEditorFromFileUri(doc.getUri(), doc.getLanguageId());
|
||||
CompilationUnit cu = getCompilationUnit(doc);
|
||||
assertNotNull(cu);
|
||||
|
||||
harness.changeDocument(doc.getUri(), 0, 0, " ");
|
||||
CompilationUnit cuAnother = getCompilationUnit(doc);
|
||||
assertNotNull(cuAnother);
|
||||
assertFalse(cu == cuAnother);
|
||||
|
||||
CompilationUnit cuYetAnother = getCompilationUnit(doc);
|
||||
assertTrue(cuAnother == cuYetAnother);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cu_cache_invalidated_by_doc_close() throws Exception {
|
||||
harness = BootLanguageServerHarness.builder()
|
||||
.mockDefaults().build();
|
||||
harness.useProject(new IJavaProject() {
|
||||
@Override
|
||||
public IClasspath getClasspath() {
|
||||
return new DelegatingCachedClasspath<>(() -> null, null);
|
||||
}
|
||||
});
|
||||
harness.intialize(null);
|
||||
|
||||
TextDocument doc = new TextDocument(harness.createTempUri(), LanguageId.JAVA, 0, "package my.package\n" +
|
||||
"\n" +
|
||||
"public class SomeClass {\n" +
|
||||
"\n" +
|
||||
"}\n");
|
||||
|
||||
harness.newEditorFromFileUri(doc.getUri(), doc.getLanguageId());
|
||||
CompilationUnit cu = getCompilationUnit(doc);
|
||||
assertNotNull(cu);
|
||||
|
||||
harness.closeDocument(doc.getId());
|
||||
CompilationUnit cuAnother = getCompilationUnit(doc);
|
||||
assertNotNull(cuAnother);
|
||||
assertFalse(cu == cuAnother);
|
||||
|
||||
CompilationUnit cuYetAnother = getCompilationUnit(doc);
|
||||
assertTrue(cuAnother == cuYetAnother);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cu_cache_invalidated_by_project_change() throws Exception {
|
||||
File directory = new File(
|
||||
ProjectsHarness.class.getResource("/test-projects/test-request-mapping-live-hover/").toURI());
|
||||
String docUri = directory.toPath().resolve("src/main/java/example/HelloWorldController.java").toUri().toString();
|
||||
|
||||
harness.intialize(directory);
|
||||
|
||||
URI fileUri = new URI(docUri);
|
||||
Path path = Paths.get(fileUri);
|
||||
String content = new String(Files.readAllBytes(path));
|
||||
|
||||
TextDocument document = new TextDocument(docUri, LanguageId.JAVA, 0, content);
|
||||
|
||||
CompilationUnit cu = getCompilationUnit(document);
|
||||
assertNotNull(cu);
|
||||
CompilationUnit cuAnother = getCompilationUnit(document);
|
||||
assertTrue(cu == cuAnother);
|
||||
|
||||
harness.changeFile(directory.toPath().resolve(MavenCore.POM_XML).toUri().toString());
|
||||
cuAnother = getCompilationUnit(document);
|
||||
assertNotNull(cuAnother);
|
||||
assertFalse(cu == cuAnother);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cu_cache_invalidated_by_project_deletion() throws Exception {
|
||||
File directory = new File(
|
||||
ProjectsHarness.class.getResource("/test-projects/test-request-mapping-live-hover/").toURI());
|
||||
String docUri = directory.toPath().resolve("src/main/java/example/HelloWorldController.java").toUri().toString();
|
||||
|
||||
harness.intialize(directory);
|
||||
|
||||
URI fileUri = new URI(docUri);
|
||||
Path path = Paths.get(fileUri);
|
||||
String content = new String(Files.readAllBytes(path));
|
||||
|
||||
TextDocument document = new TextDocument(docUri, LanguageId.JAVA, 0, content);
|
||||
|
||||
CompilationUnit cu = getCompilationUnit(document);
|
||||
assertNotNull(cu);
|
||||
CompilationUnit cuAnother = getCompilationUnit(document);
|
||||
assertTrue(cu == cuAnother);
|
||||
|
||||
harness.deleteFile(directory.toPath().resolve(MavenCore.POM_XML).toUri().toString());
|
||||
cuAnother = getCompilationUnit(document);
|
||||
assertNotNull(cuAnother);
|
||||
assertFalse(cu == cuAnother);
|
||||
}
|
||||
}
|
||||
@@ -1,354 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2018 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.utils.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.eclipse.lsp4j.SymbolInformation;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.ide.vscode.boot.java.Annotations;
|
||||
import org.springframework.ide.vscode.boot.java.BootJavaLanguageServer;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider;
|
||||
import org.springframework.ide.vscode.boot.java.requestmapping.RequestMappingSymbolProvider;
|
||||
import org.springframework.ide.vscode.boot.java.utils.SpringIndexer;
|
||||
import org.springframework.ide.vscode.commons.maven.MavenCore;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness;
|
||||
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class SpringIndexerTest {
|
||||
|
||||
private Map<String, SymbolProvider> symbolProviders;
|
||||
private LanguageServerHarness<BootJavaLanguageServer> harness;
|
||||
|
||||
private SpringIndexer indexer() {
|
||||
return harness.getServerWrapper().getSpringIndexer();
|
||||
}
|
||||
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
symbolProviders = new HashMap<>();
|
||||
symbolProviders.put(Annotations.SPRING_REQUEST_MAPPING, new RequestMappingSymbolProvider());
|
||||
harness = BootLanguageServerHarness.builder().build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScanningAllAnnotationsSimpleProjectUpfront() throws Exception {
|
||||
harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI()));
|
||||
|
||||
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI());
|
||||
|
||||
List<? extends SymbolInformation> allSymbols = indexer().getAllSymbols("");
|
||||
|
||||
assertEquals(6, allSymbols.size());
|
||||
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/MainClass.java").toUri().toString();
|
||||
assertTrue(containsSymbol(allSymbols, "@+ 'mainClass' (@SpringBootApplication <: @SpringBootConfiguration, @Configuration, @Component) MainClass", docUri, 6, 0, 6, 22));
|
||||
assertTrue(containsSymbol(allSymbols, "@/embedded-foo-mapping", docUri, 17, 1, 17, 41));
|
||||
assertTrue(containsSymbol(allSymbols, "@/foo-root-mapping/embedded-foo-mapping-with-root", docUri, 27, 1, 27, 51));
|
||||
|
||||
docUri = directory.toPath().resolve("src/main/java/org/test/SimpleMappingClass.java").toUri().toString();
|
||||
assertTrue(containsSymbol(allSymbols, "@/mapping1", docUri, 6, 1, 6, 28));
|
||||
assertTrue(containsSymbol(allSymbols, "@/mapping2", docUri, 11, 1, 11, 28));
|
||||
|
||||
docUri = directory.toPath().resolve("src/main/java/org/test/sub/MappingClassSubpackage.java").toUri().toString();
|
||||
assertTrue(containsSymbol(allSymbols, "@/classlevel/mapping-subpackage", docUri, 7, 1, 7, 38));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRetrievingSymbolsPerDocument() throws Exception {
|
||||
harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI()));
|
||||
|
||||
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI());
|
||||
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/MainClass.java").toUri().toString();
|
||||
List<? extends SymbolInformation> symbols = indexer().getSymbols(docUri);
|
||||
assertEquals(3, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@+ 'mainClass' (@SpringBootApplication <: @SpringBootConfiguration, @Configuration, @Component) MainClass", docUri, 6, 0, 6, 22));
|
||||
assertTrue(containsSymbol(symbols, "@/embedded-foo-mapping", docUri, 17, 1, 17, 41));
|
||||
assertTrue(containsSymbol(symbols, "@/foo-root-mapping/embedded-foo-mapping-with-root", docUri, 27, 1, 27, 51));
|
||||
|
||||
docUri = directory.toPath().resolve("src/main/java/org/test/SimpleMappingClass.java").toUri().toString();
|
||||
symbols = indexer().getSymbols(docUri);
|
||||
assertEquals(2, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/mapping1", docUri, 6, 1, 6, 28));
|
||||
assertTrue(containsSymbol(symbols, "@/mapping2", docUri, 11, 1, 11, 28));
|
||||
|
||||
docUri = directory.toPath().resolve("src/main/java/org/test/sub/MappingClassSubpackage.java").toUri().toString();
|
||||
symbols = indexer().getSymbols(docUri);
|
||||
assertEquals(1, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/classlevel/mapping-subpackage", docUri, 7, 1, 7, 38));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScanningAllAnnotationsMultiModuleProjectUpfront() throws Exception {
|
||||
harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/").toURI()));
|
||||
|
||||
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing").toURI());
|
||||
|
||||
List<? extends SymbolInformation> allSymbols = indexer().getAllSymbols("");
|
||||
|
||||
assertEquals(6, allSymbols.size());
|
||||
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/MainClass.java").toUri().toString();
|
||||
assertTrue(containsSymbol(allSymbols, "@+ 'mainClass' (@SpringBootApplication <: @SpringBootConfiguration, @Configuration, @Component) MainClass", docUri, 6, 0, 6, 22));
|
||||
assertTrue(containsSymbol(allSymbols, "@/embedded-foo-mapping", docUri, 17, 1, 17, 41));
|
||||
assertTrue(containsSymbol(allSymbols, "@/foo-root-mapping/embedded-foo-mapping-with-root", docUri, 27, 1, 27, 51));
|
||||
|
||||
docUri = directory.toPath().resolve("src/main/java/org/test/SimpleMappingClass.java").toUri().toString();
|
||||
assertTrue(containsSymbol(allSymbols, "@/mapping1", docUri, 6, 1, 6, 28));
|
||||
assertTrue(containsSymbol(allSymbols, "@/mapping2", docUri, 11, 1, 11, 28));
|
||||
|
||||
docUri = directory.toPath().resolve("src/main/java/org/test/sub/MappingClassSubpackage.java").toUri().toString();
|
||||
assertTrue(containsSymbol(allSymbols, "@/classlevel/mapping-subpackage", docUri, 7, 1, 7, 38));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateChangedDocument() throws Exception {
|
||||
harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI()));
|
||||
|
||||
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI());
|
||||
|
||||
// update document and update index
|
||||
String changedDocURI = directory.toPath().resolve("src/main/java/org/test/SimpleMappingClass.java").toUri().toString();
|
||||
|
||||
String newContent = FileUtils.readFileToString(new File(new URI(changedDocURI))).replace("mapping1", "mapping1-CHANGED");
|
||||
CompletableFuture<Void> updateFuture = indexer().updateDocument(changedDocURI, newContent);
|
||||
|
||||
updateFuture.get(5, TimeUnit.SECONDS);
|
||||
|
||||
// check for updated index per document
|
||||
List<? extends SymbolInformation> symbols = indexer().getSymbols(changedDocURI);
|
||||
assertEquals(2, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/mapping1-CHANGED", changedDocURI, 6, 1, 6, 36));
|
||||
assertTrue(containsSymbol(symbols, "@/mapping2", changedDocURI, 11, 1, 11, 28));
|
||||
|
||||
// check for updated index in all symbols
|
||||
List<? extends SymbolInformation> allSymbols = indexer().getAllSymbols("");
|
||||
assertEquals(6, allSymbols.size());
|
||||
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/MainClass.java").toUri().toString();
|
||||
assertTrue(containsSymbol(allSymbols, "@+ 'mainClass' (@SpringBootApplication <: @SpringBootConfiguration, @Configuration, @Component) MainClass", docUri, 6, 0, 6, 22));
|
||||
assertTrue(containsSymbol(allSymbols, "@/embedded-foo-mapping", docUri, 17, 1, 17, 41));
|
||||
assertTrue(containsSymbol(allSymbols, "@/foo-root-mapping/embedded-foo-mapping-with-root", docUri, 27, 1, 27, 51));
|
||||
|
||||
docUri = directory.toPath().resolve("src/main/java/org/test/SimpleMappingClass.java").toUri().toString();
|
||||
assertTrue(containsSymbol(allSymbols, "@/mapping1-CHANGED", docUri, 6, 1, 6, 36));
|
||||
assertTrue(containsSymbol(allSymbols, "@/mapping2", docUri, 11, 1, 11, 28));
|
||||
|
||||
docUri = directory.toPath().resolve("src/main/java/org/test/sub/MappingClassSubpackage.java").toUri().toString();
|
||||
assertTrue(containsSymbol(allSymbols, "@/classlevel/mapping-subpackage", docUri, 7, 1, 7, 38));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNewDocumentCreated() throws Exception {
|
||||
harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI()));
|
||||
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI());
|
||||
|
||||
String createdDocURI = directory.toPath().resolve("src/main/java/org/test/CreatedClass.java").toUri().toString();
|
||||
|
||||
// check for document to not be created yet
|
||||
List<? extends SymbolInformation> symbols = indexer().getSymbols(createdDocURI);
|
||||
assertNull(symbols);
|
||||
|
||||
List<? extends SymbolInformation> allSymbols = indexer().getAllSymbols("");
|
||||
assertEquals(6, allSymbols.size());
|
||||
|
||||
try {
|
||||
// create document and update index
|
||||
String content = "package org.test;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.web.bind.annotation.RequestMapping;\n" +
|
||||
"\n" +
|
||||
"public class SimpleMappingClass {\n" +
|
||||
" \n" +
|
||||
" @RequestMapping(\"created-mapping1\")\n" +
|
||||
" public String hello1() {\n" +
|
||||
" return \"hello1\";\n" +
|
||||
" }\n" +
|
||||
"\n" +
|
||||
" @RequestMapping(\"created-mapping2\")\n" +
|
||||
" public String hello2() {\n" +
|
||||
" return \"hello2\";\n" +
|
||||
" }\n" +
|
||||
"\n" +
|
||||
"}\n" +
|
||||
"";
|
||||
FileUtils.write(new File(new URI(createdDocURI)), content);
|
||||
CompletableFuture<Void> createFuture = indexer().createDocument(createdDocURI);
|
||||
createFuture.get(5, TimeUnit.SECONDS);
|
||||
|
||||
// check for updated index per document
|
||||
symbols = indexer().getSymbols(createdDocURI);
|
||||
assertEquals(2, symbols.size());
|
||||
assertTrue(containsSymbol(symbols, "@/created-mapping1", createdDocURI, 6, 1, 6, 36));
|
||||
assertTrue(containsSymbol(symbols, "@/created-mapping2", createdDocURI, 11, 1, 11, 36));
|
||||
|
||||
// check for updated index in all symbols
|
||||
allSymbols = indexer().getAllSymbols("");
|
||||
assertEquals(8, allSymbols.size());
|
||||
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/MainClass.java").toUri().toString();
|
||||
assertTrue(containsSymbol(allSymbols, "@+ 'mainClass' (@SpringBootApplication <: @SpringBootConfiguration, @Configuration, @Component) MainClass", docUri, 6, 0, 6, 22));
|
||||
assertTrue(containsSymbol(allSymbols, "@/embedded-foo-mapping", docUri, 17, 1, 17, 41));
|
||||
assertTrue(containsSymbol(allSymbols, "@/foo-root-mapping/embedded-foo-mapping-with-root", docUri, 27, 1, 27, 51));
|
||||
|
||||
docUri = directory.toPath().resolve("src/main/java/org/test/SimpleMappingClass.java").toUri().toString();
|
||||
assertTrue(containsSymbol(allSymbols, "@/mapping1", docUri, 6, 1, 6, 28));
|
||||
assertTrue(containsSymbol(allSymbols, "@/mapping2", docUri, 11, 1, 11, 28));
|
||||
|
||||
docUri = directory.toPath().resolve("src/main/java/org/test/sub/MappingClassSubpackage.java").toUri().toString();
|
||||
assertTrue(containsSymbol(allSymbols, "@/classlevel/mapping-subpackage", docUri, 7, 1, 7, 38));
|
||||
|
||||
assertTrue(containsSymbol(allSymbols, "@/created-mapping1", createdDocURI, 6, 1, 6, 36));
|
||||
assertTrue(containsSymbol(allSymbols, "@/created-mapping2", createdDocURI, 11, 1, 11, 36));
|
||||
}
|
||||
finally {
|
||||
FileUtils.deleteQuietly(new File(new URI(createdDocURI)));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveSymbolsFromDeletedDocument() throws Exception {
|
||||
harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI()));
|
||||
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI());
|
||||
|
||||
// update document and update index
|
||||
String deletedDocURI = directory.toPath().resolve("src/main/java/org/test/SimpleMappingClass.java").toUri().toString();
|
||||
CompletableFuture<Void> deleteFuture = indexer().deleteDocument(deletedDocURI);
|
||||
deleteFuture.get(5, TimeUnit.SECONDS);
|
||||
|
||||
// check for updated index per document
|
||||
List<? extends SymbolInformation> symbols = indexer().getSymbols(deletedDocURI);
|
||||
assertNull(symbols);
|
||||
|
||||
// check for updated index in all symbols
|
||||
List<? extends SymbolInformation> allSymbols = indexer().getAllSymbols("");
|
||||
assertEquals(4, allSymbols.size());
|
||||
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/MainClass.java").toUri().toString();
|
||||
assertTrue(containsSymbol(allSymbols, "@+ 'mainClass' (@SpringBootApplication <: @SpringBootConfiguration, @Configuration, @Component) MainClass", docUri, 6, 0, 6, 22));
|
||||
assertTrue(containsSymbol(allSymbols, "@/embedded-foo-mapping", docUri, 17, 1, 17, 41));
|
||||
assertTrue(containsSymbol(allSymbols, "@/foo-root-mapping/embedded-foo-mapping-with-root", docUri, 27, 1, 27, 51));
|
||||
|
||||
docUri = directory.toPath().resolve("src/main/java/org/test/sub/MappingClassSubpackage.java").toUri().toString();
|
||||
assertTrue(containsSymbol(allSymbols, "@/classlevel/mapping-subpackage", docUri, 7, 1, 7, 38));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilterSymbolsUsingQueryString() throws Exception {
|
||||
harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI()));
|
||||
|
||||
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI());
|
||||
|
||||
List<? extends SymbolInformation> allSymbols = indexer().getAllSymbols("mapp");
|
||||
|
||||
assertEquals(6, allSymbols.size());
|
||||
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/MainClass.java").toUri().toString();
|
||||
assertTrue(containsSymbol(allSymbols, "@/embedded-foo-mapping", docUri, 17, 1, 17, 41));
|
||||
assertTrue(containsSymbol(allSymbols, "@/foo-root-mapping/embedded-foo-mapping-with-root", docUri, 27, 1, 27, 51));
|
||||
|
||||
docUri = directory.toPath().resolve("src/main/java/org/test/SimpleMappingClass.java").toUri().toString();
|
||||
assertTrue(containsSymbol(allSymbols, "@/mapping1", docUri, 6, 1, 6, 28));
|
||||
assertTrue(containsSymbol(allSymbols, "@/mapping2", docUri, 11, 1, 11, 28));
|
||||
|
||||
docUri = directory.toPath().resolve("src/main/java/org/test/sub/MappingClassSubpackage.java").toUri().toString();
|
||||
assertTrue(containsSymbol(allSymbols, "@/classlevel/mapping-subpackage", docUri, 7, 1, 7, 38));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilterSymbolsUsingQueryStringSplittedResult() throws Exception {
|
||||
harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI()));
|
||||
|
||||
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI());
|
||||
|
||||
List<? extends SymbolInformation> allSymbols = indexer().getAllSymbols("@/foo-root-mapping");
|
||||
|
||||
assertEquals(1, allSymbols.size());
|
||||
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/MainClass.java").toUri().toString();
|
||||
|
||||
assertTrue(containsSymbol(allSymbols, "@/foo-root-mapping/embedded-foo-mapping-with-root", docUri, 27, 1, 27, 51));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilterSymbolsUsingQueryStringFullSymbolString() throws Exception {
|
||||
harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI()));
|
||||
|
||||
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI());
|
||||
|
||||
List<? extends SymbolInformation> allSymbols = indexer().getAllSymbols("@/foo-root-mapping/embedded-foo-mapping-with-root");
|
||||
|
||||
assertEquals(1, allSymbols.size());
|
||||
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/MainClass.java").toUri().toString();
|
||||
|
||||
assertTrue(containsSymbol(allSymbols, "@/foo-root-mapping/embedded-foo-mapping-with-root", docUri, 27, 1, 27, 51));
|
||||
}
|
||||
|
||||
private boolean containsSymbol(List<? extends SymbolInformation> symbols, String name, String uri, int startLine, int startCHaracter, int endLine, int endCharacter) {
|
||||
for (Iterator<? extends SymbolInformation> iterator = symbols.iterator(); iterator.hasNext();) {
|
||||
SymbolInformation symbol = iterator.next();
|
||||
|
||||
if (symbol.getName().equals(name)
|
||||
&& symbol.getLocation().getUri().equals(uri)
|
||||
&& symbol.getLocation().getRange().getStart().getLine() == startLine
|
||||
&& symbol.getLocation().getRange().getStart().getCharacter() == startCHaracter
|
||||
&& symbol.getLocation().getRange().getEnd().getLine() == endLine
|
||||
&& symbol.getLocation().getRange().getEnd().getCharacter() == endCharacter) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefreshOnProjectChange() throws Exception {
|
||||
harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI()));
|
||||
|
||||
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI());
|
||||
|
||||
List<? extends SymbolInformation> allSymbols = indexer().getAllSymbols("");
|
||||
assertEquals(6, allSymbols.size());
|
||||
|
||||
File pomFile = directory.toPath().resolve(MavenCore.POM_XML).toFile();
|
||||
|
||||
assertFalse(indexer().isInitializing());
|
||||
harness.changeFile(pomFile.toURI().toString());
|
||||
// Refresh in progress
|
||||
assertTrue(indexer().isInitializing());
|
||||
|
||||
allSymbols = indexer().getAllSymbols("");
|
||||
assertFalse(indexer().isInitializing());
|
||||
assertEquals(6, allSymbols.size());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.utils.test;
|
||||
|
||||
import static org.mockito.Matchers.anyObject;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.ide.vscode.boot.java.BootJavaLanguageServer;
|
||||
import org.springframework.ide.vscode.boot.java.metadata.DefaultSpringPropertyIndexProvider;
|
||||
import org.springframework.ide.vscode.commons.languageserver.ProgressService;
|
||||
import org.springframework.ide.vscode.commons.maven.MavenCore;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness;
|
||||
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
|
||||
|
||||
/**
|
||||
* Tests for Spring properties index in Boot Java server
|
||||
*
|
||||
* @author Alex Boyko
|
||||
*
|
||||
*/
|
||||
public class SpringPropertyIndexTest {
|
||||
|
||||
private LanguageServerHarness<BootJavaLanguageServer> harness;
|
||||
|
||||
private DefaultSpringPropertyIndexProvider propertyIndexProvider;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
harness = BootLanguageServerHarness.builder().build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPropertiesIndexRefreshOnProjectChange() throws Exception {
|
||||
harness.intialize(new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI()));
|
||||
propertyIndexProvider = (DefaultSpringPropertyIndexProvider) harness.getServerWrapper().getSpringPropertyIndexProvider();
|
||||
|
||||
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI());
|
||||
|
||||
File javaFile = new File(directory, "/src/main/java/org/test/SimpleMappingClass.java");
|
||||
|
||||
TextDocument doc = new TextDocument(javaFile.toURI().toString(), LanguageId.JAVA);
|
||||
|
||||
// Not cached yet, hence progress service invoked
|
||||
ProgressService progressService = mock(ProgressService.class);
|
||||
propertyIndexProvider.setProgressService(progressService);
|
||||
propertyIndexProvider.getIndex(doc);
|
||||
verify(progressService, atLeastOnce()).progressEvent(anyObject(), anyObject());
|
||||
|
||||
// Should be cached now, so progress service should not be touched
|
||||
progressService = mock(ProgressService.class);
|
||||
propertyIndexProvider.setProgressService(progressService);
|
||||
propertyIndexProvider.getIndex(doc);
|
||||
verify(progressService, never()).progressEvent(anyObject(), anyObject());
|
||||
|
||||
// Change POM file for the project
|
||||
harness.changeFile(new File(directory, MavenCore.POM_XML).toURI().toString());
|
||||
|
||||
// POM has changed, hence project needs to be reloaded, cached value is cleared
|
||||
progressService = mock(ProgressService.class);
|
||||
propertyIndexProvider.setProgressService(progressService);
|
||||
propertyIndexProvider.getIndex(doc);
|
||||
verify(progressService, atLeastOnce()).progressEvent(anyObject(), anyObject());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2018 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.utils.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.net.URL;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.ide.vscode.boot.java.utils.SourceLinks;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.LspClient;
|
||||
import org.springframework.ide.vscode.commons.maven.MavenBuilder;
|
||||
import org.springframework.ide.vscode.commons.maven.MavenCore;
|
||||
import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
|
||||
/**
|
||||
* Tests for creation of VSCode links in hover documentation
|
||||
*
|
||||
* @author Alex Boyko
|
||||
*
|
||||
*/
|
||||
public class VSCodeSourceLinksTest {
|
||||
|
||||
private static LoadingCache<String, MavenJavaProject> mavenProjectsCache = CacheBuilder.newBuilder().build(new CacheLoader<String, MavenJavaProject>() {
|
||||
|
||||
@Override
|
||||
public MavenJavaProject load(String projectName) throws Exception {
|
||||
Path testProjectPath = Paths.get(VSCodeSourceLinksTest.class.getResource("/test-projects/" + projectName).toURI());
|
||||
MavenBuilder.newBuilder(testProjectPath).clean().pack().javadoc().skipTests().execute();
|
||||
return new MavenJavaProject(MavenCore.getDefault(), testProjectPath.resolve(MavenCore.POM_XML).toFile());
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@Before
|
||||
public void setupAll() throws Exception {
|
||||
System.setProperty("sts.lsp.client", LspClient.Client.VSCODE.toString());
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDownAll() throws Exception {
|
||||
System.setProperty("sts.lsp.client", "");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJavaSourceUrl() throws Exception {
|
||||
MavenJavaProject project = mavenProjectsCache.get("empty-boot-15-web-app");
|
||||
Optional<String> url = SourceLinks.sourceLinkUrlForFQName(project, "com.example.EmptyBoot15WebAppApplication");
|
||||
assertTrue(url.isPresent());
|
||||
Path projectPath = Paths.get(project.pom().getParent());
|
||||
Path relativePath = projectPath.relativize(Paths.get(new URL(url.get()).toURI()));
|
||||
assertEquals(Paths.get("src/main/java/com/example/EmptyBoot15WebAppApplication.java"), relativePath);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJarUrl() throws Exception {
|
||||
MavenJavaProject project = mavenProjectsCache.get("empty-boot-15-web-app");
|
||||
Optional<String> url = SourceLinks.sourceLinkUrlForFQName(project, "org.springframework.boot.autoconfigure.SpringBootApplication");
|
||||
assertTrue(url.isPresent());
|
||||
String headerPart = url.get().substring(0, url.get().indexOf('?'));
|
||||
assertEquals("jdt://contents/spring-boot-autoconfigure-1.5.8.RELEASE.jar/org.springframework.boot.autoconfigure/SpringBootApplication.class", headerPart);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJarUrlInnerType() throws Exception {
|
||||
MavenJavaProject project = mavenProjectsCache.get("empty-boot-15-web-app");
|
||||
Optional<String> url = SourceLinks.sourceLinkUrlForFQName(project, "org.springframework.web.client.RestTemplate$AcceptHeaderRequestCallback");
|
||||
assertTrue(url.isPresent());
|
||||
String headerPart = url.get().substring(0, url.get().indexOf('?'));
|
||||
assertEquals("jdt://contents/spring-web-4.3.12.RELEASE.jar/org.springframework.web.client/RestTemplate$AcceptHeaderRequestCallback.class", headerPart);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,293 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.value.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.eclipse.lsp4j.CompletionItem;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.ide.vscode.boot.java.value.ValueCompletionProcessor;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.Editor;
|
||||
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
|
||||
import org.springframework.ide.vscode.project.harness.PropertyIndexHarness;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class ValueCompletionTest {
|
||||
|
||||
private BootLanguageServerHarness harness;
|
||||
private IJavaProject testProject;
|
||||
|
||||
private Editor editor;
|
||||
|
||||
private PropertyIndexHarness indexHarness;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
//Somewhat strange test setup, test provides a specific test project.
|
||||
// The context finder finds this test project,
|
||||
// But it is not used in the indexProvider.
|
||||
//This is a bit odd... but we preserved the strangeness how it was.
|
||||
testProject = ProjectsHarness.INSTANCE.mavenProject("test-annotations");
|
||||
harness = BootLanguageServerHarness.builder()
|
||||
.mockDefaults()
|
||||
.projectFinder(d -> Optional.ofNullable(getTestProject()))
|
||||
.build();
|
||||
indexHarness = harness.getPropertyIndexHarness();
|
||||
harness.intialize(null);
|
||||
}
|
||||
|
||||
private IJavaProject getTestProject() {
|
||||
return testProject;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrefixIdentification() {
|
||||
ValueCompletionProcessor processor = new ValueCompletionProcessor(null);
|
||||
|
||||
assertEquals("pre", processor.identifyPropertyPrefix("pre", 3));
|
||||
assertEquals("pre", processor.identifyPropertyPrefix("prefix", 3));
|
||||
assertEquals("", processor.identifyPropertyPrefix("", 0));
|
||||
assertEquals("pre", processor.identifyPropertyPrefix("$pre", 4));
|
||||
|
||||
assertEquals("", processor.identifyPropertyPrefix("${pre", 0));
|
||||
assertEquals("", processor.identifyPropertyPrefix("${pre", 1));
|
||||
assertEquals("", processor.identifyPropertyPrefix("${pre", 2));
|
||||
assertEquals("p", processor.identifyPropertyPrefix("${pre", 3));
|
||||
assertEquals("pr", processor.identifyPropertyPrefix("${pre", 4));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyBracketsCompletion() throws Exception {
|
||||
prepareCase("@Value(\"onField\")", "@Value(<*>)");
|
||||
prepareDefaultIndexData();
|
||||
|
||||
assertAnnotationCompletions(
|
||||
"@Value(\"${data.prop2}\"<*>)",
|
||||
"@Value(\"${else.prop3}\"<*>)",
|
||||
"@Value(\"${spring.prop1}\"<*>)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyBracketsCompletionWithParamName() throws Exception {
|
||||
prepareCase("@Value(\"onField\")", "@Value(value=<*>)");
|
||||
prepareDefaultIndexData();
|
||||
|
||||
assertAnnotationCompletions(
|
||||
"@Value(value=\"${data.prop2}\"<*>)",
|
||||
"@Value(value=\"${else.prop3}\"<*>)",
|
||||
"@Value(value=\"${spring.prop1}\"<*>)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyBracketsCompletionWithWrongParamName() throws Exception {
|
||||
prepareCase("@Value(\"onField\")", "@Value(another=<*>)");
|
||||
prepareDefaultIndexData();
|
||||
assertAnnotationCompletions();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnlyDollarNoQoutesCompletion() throws Exception {
|
||||
prepareCase("@Value(\"onField\")", "@Value($<*>)");
|
||||
prepareDefaultIndexData();
|
||||
|
||||
assertAnnotationCompletions(
|
||||
"@Value(\"${data.prop2}\"<*>)",
|
||||
"@Value(\"${else.prop3}\"<*>)",
|
||||
"@Value(\"${spring.prop1}\"<*>)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnlyDollarNoQoutesWithParamCompletion() throws Exception {
|
||||
prepareCase("@Value(\"onField\")", "@Value(value=$<*>)");
|
||||
prepareDefaultIndexData();
|
||||
|
||||
assertAnnotationCompletions(
|
||||
"@Value(value=\"${data.prop2}\"<*>)",
|
||||
"@Value(value=\"${else.prop3}\"<*>)",
|
||||
"@Value(value=\"${spring.prop1}\"<*>)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnlyDollarCompletion() throws Exception {
|
||||
prepareCase("@Value(\"onField\")", "@Value(\"$<*>\")");
|
||||
prepareDefaultIndexData();
|
||||
|
||||
assertAnnotationCompletions(
|
||||
"@Value(\"${data.prop2}<*>\")",
|
||||
"@Value(\"${else.prop3}<*>\")",
|
||||
"@Value(\"${spring.prop1}<*>\")");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnlyDollarWithParamCompletion() throws Exception {
|
||||
prepareCase("@Value(\"onField\")", "@Value(value=\"$<*>\")");
|
||||
prepareDefaultIndexData();
|
||||
|
||||
assertAnnotationCompletions(
|
||||
"@Value(value=\"${data.prop2}<*>\")",
|
||||
"@Value(value=\"${else.prop3}<*>\")",
|
||||
"@Value(value=\"${spring.prop1}<*>\")");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDollarWithBracketsCompletion() throws Exception {
|
||||
prepareCase("@Value(\"onField\")", "@Value(\"${<*>}\")");
|
||||
prepareDefaultIndexData();
|
||||
|
||||
assertAnnotationCompletions(
|
||||
"@Value(\"${data.prop2<*>}\")",
|
||||
"@Value(\"${else.prop3<*>}\")",
|
||||
"@Value(\"${spring.prop1<*>}\")");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDollarWithBracketsWithParamCompletion() throws Exception {
|
||||
prepareCase("@Value(\"onField\")", "@Value(value=\"${<*>}\")");
|
||||
prepareDefaultIndexData();
|
||||
|
||||
assertAnnotationCompletions(
|
||||
"@Value(value=\"${data.prop2<*>}\")",
|
||||
"@Value(value=\"${else.prop3<*>}\")",
|
||||
"@Value(value=\"${spring.prop1<*>}\")");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyStringLiteralCompletion() throws Exception {
|
||||
prepareCase("@Value(\"onField\")", "@Value(\"<*>\")");
|
||||
prepareDefaultIndexData();
|
||||
|
||||
assertAnnotationCompletions(
|
||||
"@Value(\"${data.prop2}<*>\")",
|
||||
"@Value(\"${else.prop3}<*>\")",
|
||||
"@Value(\"${spring.prop1}<*>\")");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPlainPrefixCompletion() throws Exception {
|
||||
prepareCase("@Value(\"onField\")", "@Value(spri<*>)");
|
||||
prepareDefaultIndexData();
|
||||
|
||||
assertAnnotationCompletions(
|
||||
"@Value(\"${spring.prop1}\"<*>)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQoutedPrefixCompletion() throws Exception {
|
||||
prepareCase("@Value(\"onField\")", "@Value(\"spri<*>\")");
|
||||
prepareDefaultIndexData();
|
||||
|
||||
assertAnnotationCompletions(
|
||||
"@Value(\"${spring.prop1}<*>\")");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRandomSpelExpressionNoCompletion() throws Exception {
|
||||
prepareCase("@Value(\"onField\")", "@Value(\"#{<*>}\")");
|
||||
prepareDefaultIndexData();
|
||||
|
||||
assertAnnotationCompletions(
|
||||
"@Value(\"#{${data.prop2}<*>}\")",
|
||||
"@Value(\"#{${else.prop3}<*>}\")",
|
||||
"@Value(\"#{${spring.prop1}<*>}\")");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRandomSpelExpressionWithPropertyDollar() throws Exception {
|
||||
prepareCase("@Value(\"onField\")", "@Value(\"#{345$<*>}\")");
|
||||
prepareDefaultIndexData();
|
||||
|
||||
assertAnnotationCompletions(
|
||||
"@Value(\"#{345${data.prop2}<*>}\")",
|
||||
"@Value(\"#{345${else.prop3}<*>}\")",
|
||||
"@Value(\"#{345${spring.prop1}<*>}\")");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRandomSpelExpressionWithPropertyDollerWithoutClosindBracket() throws Exception {
|
||||
prepareCase("@Value(\"onField\")", "@Value(\"#{345${<*>}\")");
|
||||
prepareDefaultIndexData();
|
||||
|
||||
assertAnnotationCompletions(
|
||||
"@Value(\"#{345${data.prop2}<*>}\")",
|
||||
"@Value(\"#{345${else.prop3}<*>}\")",
|
||||
"@Value(\"#{345${spring.prop1}<*>}\")");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRandomSpelExpressionWithPropertyDollerWithClosingBracket() throws Exception {
|
||||
prepareCase("@Value(\"onField\")", "@Value(\"#{345${<*>}}\")");
|
||||
prepareDefaultIndexData();
|
||||
|
||||
assertAnnotationCompletions(
|
||||
"@Value(\"#{345${data.prop2<*>}}\")",
|
||||
"@Value(\"#{345${else.prop3<*>}}\")",
|
||||
"@Value(\"#{345${spring.prop1<*>}}\")");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRandomSpelExpressionWithPropertyPrefixWithoutClosingBracket() throws Exception {
|
||||
prepareCase("@Value(\"onField\")", "@Value(\"#{345${spri<*>}\")");
|
||||
prepareDefaultIndexData();
|
||||
|
||||
assertAnnotationCompletions(
|
||||
"@Value(\"#{345${spring.prop1}<*>}\")");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRandomSpelExpressionWithPropertyPrefixWithClosingBracket() throws Exception {
|
||||
prepareCase("@Value(\"onField\")", "@Value(\"#{345${spri<*>}}\")");
|
||||
prepareDefaultIndexData();
|
||||
|
||||
assertAnnotationCompletions(
|
||||
"@Value(\"#{345${spring.prop1<*>}}\")");
|
||||
}
|
||||
|
||||
private void prepareDefaultIndexData() {
|
||||
indexHarness.data("spring.prop1", "java.lang.String", null, null);
|
||||
indexHarness.data("data.prop2", "java.lang.String", null, null);
|
||||
indexHarness.data("else.prop3", "java.lang.String", null, null);
|
||||
}
|
||||
|
||||
private void prepareCase(String selectedAnnotation, String annotationStatementBeforeTest) throws Exception {
|
||||
InputStream resource = this.getClass().getResourceAsStream("/test-projects/test-annotations/src/main/java/org/test/TestValueCompletion.java");
|
||||
String content = IOUtils.toString(resource);
|
||||
|
||||
content = content.replace(selectedAnnotation, annotationStatementBeforeTest);
|
||||
editor = new Editor(harness, content, LanguageId.JAVA);
|
||||
}
|
||||
|
||||
private void assertAnnotationCompletions(String... completedAnnotations) throws Exception {
|
||||
List<CompletionItem> completions = editor.getCompletions();
|
||||
int i = 0;
|
||||
for (String expectedCompleted : completedAnnotations) {
|
||||
Editor clonedEditor = editor.clone();
|
||||
clonedEditor.apply(completions.get(i++));
|
||||
if (!clonedEditor.getText().contains(expectedCompleted)) {
|
||||
fail("Not found '"+expectedCompleted+"' in \n"+clonedEditor.getText());
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals(i, completions.size());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.value.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.ide.vscode.boot.java.value.ValueHoverProvider;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class ValueHoverTest {
|
||||
|
||||
@Test
|
||||
public void testGetPropertyFromValue() {
|
||||
ValueHoverProvider provider = new ValueHoverProvider();
|
||||
|
||||
assertNull(provider.getPropertyKey("${spring}", 0));
|
||||
assertNull(provider.getPropertyKey("${spring}", 1));
|
||||
assertEquals("spring", provider.getPropertyKey("${spring}", 2));
|
||||
assertEquals("spring", provider.getPropertyKey("${spring}", 3));
|
||||
assertEquals("spring", provider.getPropertyKey("${spring}", 8));
|
||||
assertNull(provider.getPropertyKey("${spring}", 9));
|
||||
|
||||
assertNull(provider.getPropertyKey("abc ${spring} and other stuff", 0));
|
||||
assertNull(provider.getPropertyKey("abc ${spring} and other stuff", 5));
|
||||
assertEquals("spring", provider.getPropertyKey("abc ${spring} and other stuff", 6));
|
||||
assertEquals("spring", provider.getPropertyKey("abc ${spring} and other stuff", 12));
|
||||
assertNull(provider.getPropertyKey("abc ${spring} and other stuff", 13));
|
||||
|
||||
assertNull(provider.getPropertyKey("abc ${spring} and ${boot} other stuff", 5));
|
||||
assertEquals("spring", provider.getPropertyKey("abc ${spring} and ${boot} other stuff", 6));
|
||||
assertEquals("spring", provider.getPropertyKey("abc ${spring} and ${boot} other stuff", 12));
|
||||
assertNull(provider.getPropertyKey("abc ${spring} and ${boot} other stuff", 13));
|
||||
|
||||
assertNull(provider.getPropertyKey("abc ${spring} and ${boot} other stuff", 19));
|
||||
assertEquals("boot", provider.getPropertyKey("abc ${spring} and ${boot} other stuff", 20));
|
||||
assertEquals("boot", provider.getPropertyKey("abc ${spring} and ${boot} other stuff", 24));
|
||||
assertNull(provider.getPropertyKey("abc ${spring} and ${boot} other stuff", 25));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.project.harness;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.time.Duration;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.springframework.ide.vscode.boot.java.BootJavaLanguageServer;
|
||||
import org.springframework.ide.vscode.boot.java.BootJavaLanguageServerParams;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.RunningAppProvider;
|
||||
import org.springframework.ide.vscode.boot.java.metadata.SpringPropertyIndexProvider;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.LSFactory;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness;
|
||||
|
||||
public class BootLanguageServerHarness extends LanguageServerHarness<BootJavaLanguageServer> {
|
||||
|
||||
private PropertyIndexHarness indexHarness;
|
||||
private final JavaProjectFinder projectFinder = (doc) -> getServerWrapper().getProjectFinder().find(doc);
|
||||
|
||||
/**
|
||||
* Creates a builder and initializes it so that it sets up a test harness with
|
||||
* the 'real stuff'. I.e project finder and other injected components are like
|
||||
* they would be in 'production' environment.
|
||||
* <p>
|
||||
* Builder methods can still be called to replace some of the components with
|
||||
* mocks selectively.
|
||||
*/
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
LSFactory<BootJavaLanguageServerParams> defaultsFactory = BootJavaLanguageServerParams.createTestDefault();
|
||||
private JavaProjectFinder projectFinder = null;
|
||||
private ProjectObserver projectObserver = null;
|
||||
private SpringPropertyIndexProvider indexProvider = null;
|
||||
private RunningAppProvider runningAppProvider = null;
|
||||
private PropertyIndexHarness indexHarness = null;
|
||||
private Duration watchDogInterval = null;
|
||||
|
||||
public BootLanguageServerHarness build() throws Exception {
|
||||
BootLanguageServerHarness harness = new BootLanguageServerHarness(this);
|
||||
return harness;
|
||||
}
|
||||
|
||||
public Builder mockDefaults() {
|
||||
indexHarness = new PropertyIndexHarness();
|
||||
projectFinder = indexHarness.getProjectFinder();
|
||||
indexProvider = indexHarness.getIndexProvider();
|
||||
projectObserver = ProjectObserver.NULL;
|
||||
runningAppProvider = RunningAppProvider.NULL;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder runningAppProvider(RunningAppProvider provider) {
|
||||
this.runningAppProvider = provider;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder projectFinder(JavaProjectFinder projectFinder) {
|
||||
this.projectFinder = projectFinder;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder propertyIndexProvider(SpringPropertyIndexProvider propertyIndexProvider) {
|
||||
this.indexProvider = propertyIndexProvider;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder watchDogInterval(Duration watchDogInterval) {
|
||||
this.watchDogInterval = watchDogInterval;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This constructor is private. Use the builder api instead.
|
||||
*/
|
||||
private BootLanguageServerHarness(Builder builder) throws Exception {
|
||||
super(() -> {
|
||||
LSFactory<BootJavaLanguageServerParams> params = (server) -> {
|
||||
BootJavaLanguageServerParams defaults = BootJavaLanguageServerParams.createTestDefault().create(server);
|
||||
return new BootJavaLanguageServerParams(
|
||||
builder.projectFinder==null?defaults.projectFinder:builder.projectFinder,
|
||||
builder.projectObserver==null?defaults.projectObserver:builder.projectObserver,
|
||||
builder.indexProvider==null?defaults.indexProvider:builder.indexProvider,
|
||||
builder.runningAppProvider==null?defaults.runningAppProvider:builder.runningAppProvider,
|
||||
builder.watchDogInterval==null?defaults.watchDogInterval:builder.watchDogInterval
|
||||
);
|
||||
};
|
||||
return new BootJavaLanguageServer(params);
|
||||
});
|
||||
this.indexHarness = builder.indexHarness;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected String getFileExtension() {
|
||||
return ".java";
|
||||
}
|
||||
|
||||
public JavaProjectFinder getProjectFinder() {
|
||||
return projectFinder;
|
||||
}
|
||||
|
||||
public PropertyIndexHarness getPropertyIndexHarness() {
|
||||
Assert.assertNotNull(indexHarness); //only supported in some types of instantations of the harness (i.e. when indexer is controlled by indexer harness.
|
||||
return indexHarness;
|
||||
}
|
||||
|
||||
public void useProject(IJavaProject p) throws Exception {
|
||||
indexHarness.useProject(p);
|
||||
}
|
||||
|
||||
public Path getOutputFolder() {
|
||||
return getProjectFinder().find(null).get().getClasspath().getOutputFolder();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.project.harness;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.RunningAppProvider;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBeansModel;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.requestmappings.RequestMapping;
|
||||
import org.springframework.ide.vscode.commons.util.ExceptionUtil;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
public class MockRunningAppProvider {
|
||||
|
||||
public final RunningAppProvider provider = mock(RunningAppProvider.class);
|
||||
public final Collection<SpringBootApp> mockedApps = new ArrayList<>();
|
||||
|
||||
public MockRunningAppProvider() {
|
||||
try {
|
||||
when(provider.getAllRunningSpringApps()).thenReturn(mockedApps);
|
||||
} catch (Exception e) {
|
||||
throw ExceptionUtil.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the mocks. Use this if the default's programmed into the mocks don't
|
||||
* suite your test case.
|
||||
* <p>
|
||||
* Note: you may also choose to call {@link Mockito}.mock directly if you do not
|
||||
* want to reset all of the mocks.
|
||||
*/
|
||||
public void reset() throws Exception {
|
||||
mockedApps.clear();
|
||||
Mockito.reset(provider);
|
||||
}
|
||||
|
||||
public MockAppBuilder builder() {
|
||||
return new MockAppBuilder(this);
|
||||
}
|
||||
|
||||
public static class MockAppBuilder {
|
||||
public final SpringBootApp app = mock(SpringBootApp.class);
|
||||
private MockRunningAppProvider runningAppProvider;
|
||||
private String processId;
|
||||
private String processName;
|
||||
|
||||
public MockAppBuilder(MockRunningAppProvider runningAppProvider) {
|
||||
this.runningAppProvider = runningAppProvider;
|
||||
}
|
||||
|
||||
public MockAppBuilder enviroment(String env) throws Exception {
|
||||
when(app.getEnvironment()).thenReturn(env);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MockAppBuilder beans(String beans) throws Exception {
|
||||
return beans(LiveBeansModel.parse(beans));
|
||||
}
|
||||
|
||||
public MockAppBuilder beans(LiveBeansModel beans) {
|
||||
when(app.getBeans()).thenReturn(beans);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MockAppBuilder processId(String processId) {
|
||||
this.processId = processId;
|
||||
when(app.getProcessID()).thenReturn(processId);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MockAppBuilder processName(String name) {
|
||||
this.processName = name;
|
||||
when(app.getProcessName()).thenReturn(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MockAppBuilder port(String port) throws Exception {
|
||||
when(app.getPort()).thenReturn(port);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MockAppBuilder host(String host) throws Exception {
|
||||
when(app.getHost()).thenReturn(host);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MockAppBuilder isSpringBootApp(boolean isBoot) throws Exception {
|
||||
when(app.isSpringBootApp()).thenReturn(isBoot);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MockAppBuilder requestMappings(String mappings) throws Exception {
|
||||
Collection<RequestMapping> requestMappings = SpringBootApp.parseRequestMappingsJson(mappings, "1.x");
|
||||
when(app.getRequestMappings()).thenReturn(requestMappings);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MockAppBuilder liveConditionalsJson(String rawJson) throws Exception{
|
||||
when(app.getLiveConditionals()).thenReturn(SpringBootApp.getLiveConditionals(rawJson, processId, processName));
|
||||
return this;
|
||||
}
|
||||
|
||||
public MockAppBuilder profiles(String... names) {
|
||||
when(app.getActiveProfiles()).thenReturn(ImmutableList.copyOf(names));
|
||||
return this;
|
||||
}
|
||||
|
||||
public MockAppBuilder profilesUnknown() {
|
||||
//Note, technically, we don't have to program the mock for this case as it will return
|
||||
// null by default. But it makes test code more readable. Also... how we represent the
|
||||
// 'unknown' case may change in the future and having this method will help fix the tests.
|
||||
when(app.getActiveProfiles()).thenReturn(null);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Builds the mock app and adds it to the app provider
|
||||
*/
|
||||
public void build() {
|
||||
runningAppProvider.mockedApps.add(app);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016, 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.project.harness;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.maven.MavenBuilder;
|
||||
import org.springframework.ide.vscode.commons.maven.MavenCore;
|
||||
import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject;
|
||||
import org.springframework.ide.vscode.commons.maven.java.classpathfile.JavaProjectWithClasspathFile;
|
||||
import org.springframework.ide.vscode.commons.util.IOUtil;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.io.Files;
|
||||
|
||||
import reactor.util.function.Tuple3;
|
||||
import reactor.util.function.Tuples;
|
||||
|
||||
/**
|
||||
* Test projects harness
|
||||
*
|
||||
* @author Alex Boyko
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
public class ProjectsHarness {
|
||||
|
||||
public static final ProjectsHarness INSTANCE = new ProjectsHarness();;
|
||||
|
||||
public Cache<Object, IJavaProject> cache = CacheBuilder.newBuilder().concurrencyLevel(1).build();
|
||||
|
||||
/**
|
||||
* A callback that is given a chance to make changes to test project contents before the test project
|
||||
* is created from it.
|
||||
*/
|
||||
public interface ProjectCustomizer {
|
||||
void customize(CustomizableProjectContent projectContent) throws Exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides convenience apis to make changes to project content from a {@link ProjectCustomizer}
|
||||
*/
|
||||
public static class CustomizableProjectContent {
|
||||
private final File projectRoot;
|
||||
|
||||
public CustomizableProjectContent(File projectRoot) {
|
||||
this.projectRoot = projectRoot;
|
||||
}
|
||||
|
||||
public void createFile(String path, String content) throws Exception {
|
||||
File target = new File(projectRoot, path);
|
||||
IOUtil.pipe(new ByteArrayInputStream(content.getBytes("UTF8")), target);
|
||||
}
|
||||
|
||||
public void createType(String fqName, String sourceCode) throws Exception {
|
||||
String sourceFile = sourceFolder()+"/"+fqName.replace('.', '/')+".java";
|
||||
createFile(sourceFile, sourceCode);
|
||||
}
|
||||
|
||||
private String sourceFolder() {
|
||||
return "src/main/java";
|
||||
}
|
||||
}
|
||||
|
||||
private enum ProjectType {
|
||||
MAVEN,
|
||||
CLASSPATH_TXT
|
||||
}
|
||||
|
||||
private ProjectsHarness() {
|
||||
}
|
||||
|
||||
public IJavaProject project(ProjectType type, String name, ProjectCustomizer customizer) throws Exception {
|
||||
Tuple3<ProjectType, String, ProjectCustomizer> key = Tuples.of(type, name, customizer);
|
||||
return cache.get(key, () -> {
|
||||
Path baseProjectPath = getProjectPath(name);
|
||||
File testProjectRoot = Files.createTempDir();
|
||||
FileUtils.copyDirectory(baseProjectPath.toFile(), testProjectRoot);
|
||||
customizer.customize(new CustomizableProjectContent(testProjectRoot));
|
||||
return createProject(type, testProjectRoot.toPath());
|
||||
});
|
||||
}
|
||||
|
||||
private IJavaProject createProject(ProjectType type, Path testProjectPath) throws Exception {
|
||||
switch (type) {
|
||||
case MAVEN:
|
||||
MavenBuilder.newBuilder(testProjectPath).clean().pack().javadoc().skipTests().execute();
|
||||
return new MavenJavaProject(MavenCore.getDefault(), testProjectPath.resolve(MavenCore.POM_XML).toFile());
|
||||
case CLASSPATH_TXT:
|
||||
MavenBuilder.newBuilder(testProjectPath).clean().pack().skipTests().execute();
|
||||
return new JavaProjectWithClasspathFile(testProjectPath.resolve(MavenCore.CLASSPATH_TXT).toFile());
|
||||
default:
|
||||
throw new IllegalStateException("Bug!!! Missing case");
|
||||
}
|
||||
}
|
||||
|
||||
public IJavaProject project(ProjectType type, String name) throws Exception {
|
||||
return cache.get(type + "/" + name, () -> {
|
||||
Path testProjectPath = getProjectPath(name);
|
||||
return createProject(type, testProjectPath);
|
||||
});
|
||||
}
|
||||
|
||||
protected Path getProjectPath(String name) throws URISyntaxException, IOException {
|
||||
return getProjectPathFromClasspath(name);
|
||||
}
|
||||
|
||||
private Path getProjectPathFromClasspath(String name) throws URISyntaxException, IOException {
|
||||
URI resource = ProjectsHarness.class.getResource("/test-projects/" + name).toURI();
|
||||
return Paths.get(resource);
|
||||
}
|
||||
|
||||
public MavenJavaProject mavenProject(String name, ProjectCustomizer customizer) throws Exception {
|
||||
return (MavenJavaProject) project(ProjectType.MAVEN, name, customizer);
|
||||
}
|
||||
|
||||
public MavenJavaProject mavenProject(String name) throws Exception {
|
||||
return (MavenJavaProject) project(ProjectType.MAVEN, name);
|
||||
}
|
||||
|
||||
public IJavaProject javaProjectWithClasspathFile(String name) throws Exception {
|
||||
return project(ProjectType.CLASSPATH_TXT, name);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,568 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.project.harness;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty;
|
||||
import org.springframework.boot.configurationmetadata.Deprecation;
|
||||
import org.springframework.boot.configurationmetadata.ValueHint;
|
||||
import org.springframework.boot.configurationmetadata.ValueProvider;
|
||||
import org.springframework.ide.vscode.boot.java.metadata.SpringPropertyIndex;
|
||||
import org.springframework.ide.vscode.boot.java.metadata.SpringPropertyIndexProvider;
|
||||
import org.springframework.ide.vscode.commons.java.IClasspath;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
import org.springframework.ide.vscode.commons.util.FuzzyMap;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
|
||||
/**
|
||||
* Provides some convenience apis for test code to create / use test data for a SpringPropertyIndex.
|
||||
*/
|
||||
public class PropertyIndexHarness {
|
||||
|
||||
private Map<String, ConfigurationMetadataProperty> datas = new LinkedHashMap<>();
|
||||
private SpringPropertyIndex index = null;
|
||||
private IJavaProject testProject = null;
|
||||
|
||||
protected final SpringPropertyIndexProvider indexProvider = new SpringPropertyIndexProvider() {
|
||||
@Override
|
||||
public FuzzyMap<ConfigurationMetadataProperty> getIndex(IDocument doc) {
|
||||
synchronized (PropertyIndexHarness.this) {
|
||||
if (index==null) {
|
||||
IClasspath classpath = testProject == null ? null : testProject.getClasspath();
|
||||
index = new SpringPropertyIndex(classpath);
|
||||
for (ConfigurationMetadataProperty propertyInfo : datas.values()) {
|
||||
index.add(propertyInfo);
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public synchronized void useProject(IJavaProject p) throws Exception {
|
||||
index = null;
|
||||
this.testProject = p;
|
||||
}
|
||||
|
||||
public class ItemConfigurer {
|
||||
|
||||
private ConfigurationMetadataProperty item;
|
||||
|
||||
public ItemConfigurer(ConfigurationMetadataProperty item) {
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a provider with a single parameter.
|
||||
* @return
|
||||
*/
|
||||
public ItemConfigurer provider(String name, String paramName, Object paramValue) {
|
||||
ValueProvider provider = new ValueProvider();
|
||||
provider.setName(name);
|
||||
provider.getParameters().put(paramName, paramValue);
|
||||
item.getHints().getValueProviders().add(provider);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a value hint. If description contains a '.' the dot is used
|
||||
* to break description into a short and long description.
|
||||
* @return
|
||||
*/
|
||||
public ItemConfigurer valueHint(Object value, String description) {
|
||||
ValueHint hint = new ValueHint();
|
||||
hint.setValue(value);
|
||||
if (description!=null) {
|
||||
int dotPos = description.indexOf('.');
|
||||
if (dotPos>=0) {
|
||||
hint.setShortDescription( description.substring(0, dotPos));
|
||||
}
|
||||
hint.setDescription(description);
|
||||
}
|
||||
item.getHints().getValueHints().add(hint);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public synchronized ItemConfigurer data(String id, String type, Object deflt, String description,
|
||||
String... source
|
||||
) {
|
||||
ConfigurationMetadataProperty item = new ConfigurationMetadataProperty();
|
||||
item.setId(id);
|
||||
item.setDescription(description);
|
||||
item.setType(type);
|
||||
item.setDefaultValue(deflt);
|
||||
index = null;
|
||||
datas.put(item.getId(), item);
|
||||
return new ItemConfigurer(item);
|
||||
}
|
||||
|
||||
public synchronized void keyHints(String id, String... hintValues) {
|
||||
index = null;
|
||||
List<ValueHint> hints = datas.get(id).getHints().getKeyHints();
|
||||
for (String value : hintValues) {
|
||||
ValueHint hint = new ValueHint();
|
||||
hint.setValue(value);
|
||||
hints.add(hint);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void valueHints(String id, String... hintValues) {
|
||||
index = null;
|
||||
List<ValueHint> hints = datas.get(id).getHints().getValueHints();
|
||||
for (String value : hintValues) {
|
||||
ValueHint hint = new ValueHint();
|
||||
hint.setValue(value);
|
||||
hints.add(hint);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void deprecate(String key, String replacedBy, String reason) {
|
||||
index = null;
|
||||
ConfigurationMetadataProperty info = datas.get(key);
|
||||
Deprecation d = new Deprecation();
|
||||
d.setReplacement(replacedBy);
|
||||
d.setReason(reason);
|
||||
info.setDeprecation(d);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this method to add some default test data to the Completion engine's index.
|
||||
* Note that this data is not added automatically, some test may want to use smaller
|
||||
* test data sets.
|
||||
*/
|
||||
public void defaultTestData() {
|
||||
data("banner.charset", "java.nio.charset.Charset", "UTF-8", "Banner file encoding.");
|
||||
data("banner.location", "java.lang.String", "classpath:banner.txt", "Banner file location.");
|
||||
data("debug", "java.lang.Boolean", "false", "Enable debug logs.");
|
||||
data("flyway.check-location", "java.lang.Boolean", "false", "Check that migration scripts location exists.");
|
||||
data("flyway.clean-on-validation-error", "java.lang.Boolean", null, null);
|
||||
data("flyway.enabled", "java.lang.Boolean", "true", "Enable flyway.");
|
||||
data("flyway.encoding", "java.lang.String", null, null);
|
||||
data("flyway.ignore-failed-future-migration", "java.lang.Boolean", null, null);
|
||||
data("flyway.init-description", "java.lang.String", null, null);
|
||||
data("flyway.init-on-migrate", "java.lang.Boolean", null, null);
|
||||
data("flyway.init-sqls", "java.util.List<java.lang.String>", null, "SQL statements to execute to initialize a connection immediately after obtaining\n it.");
|
||||
data("flyway.init-version", "org.flywaydb.core.api.MigrationVersion", null, null);
|
||||
data("flyway.locations", "java.util.List<java.lang.String>", null, "Locations of migrations scripts.");
|
||||
data("flyway.out-of-order", "java.lang.Boolean", null, null);
|
||||
data("flyway.password", "java.lang.String", null, "Login password of the database to migrate.");
|
||||
data("flyway.placeholder-prefix", "java.lang.String", null, null);
|
||||
data("flyway.placeholders", "java.util.Map<java.lang.String,java.lang.String>", null, null);
|
||||
data("flyway.placeholder-suffix", "java.lang.String", null, null);
|
||||
data("flyway.schemas", "java.lang.String[]", null, null);
|
||||
data("flyway.sql-migration-prefix", "java.lang.String", null, null);
|
||||
data("flyway.sql-migration-separator", "java.lang.String", null, null);
|
||||
data("flyway.sql-migration-suffix", "java.lang.String", null, null);
|
||||
data("flyway.table", "java.lang.String", null, null);
|
||||
data("flyway.target", "org.flywaydb.core.api.MigrationVersion", null, null);
|
||||
data("flyway.url", "java.lang.String", null, "JDBC url of the database to migrate. If not set, the primary configured data source\n is used.");
|
||||
data("flyway.user", "java.lang.String", null, "Login user of the database to migrate.");
|
||||
data("flyway.validate-on-migrate", "java.lang.Boolean", null, null);
|
||||
data("http.mappers.json-pretty-print", "java.lang.Boolean", null, "Enable json pretty print.");
|
||||
data("http.mappers.json-sort-keys", "java.lang.Boolean", null, "Enable key sorting.");
|
||||
data("liquibase.change-log", "java.lang.String", "classpath:/db/changelog/db.changelog-master.yaml", "Change log configuration path.");
|
||||
data("liquibase.check-change-log-location", "java.lang.Boolean", "true", "Check the change log location exists.");
|
||||
data("liquibase.contexts", "java.lang.String", null, "Comma-separated list of runtime contexts to use.");
|
||||
data("liquibase.default-schema", "java.lang.String", null, "Default database schema.");
|
||||
data("liquibase.drop-first", "java.lang.Boolean", "false", "Drop the database schema first.");
|
||||
data("liquibase.enabled", "java.lang.Boolean", "true", "Enable liquibase support.");
|
||||
data("liquibase.password", "java.lang.String", null, "Login password of the database to migrate.");
|
||||
data("liquibase.url", "java.lang.String", null, "JDBC url of the database to migrate. If not set, the primary configured data source\n is used.");
|
||||
data("liquibase.user", "java.lang.String", null, "Login user of the database to migrate.");
|
||||
data("logging.config", "java.lang.String", null, "Location of the logging configuration file.");
|
||||
data("logging.file", "java.lang.String", null, "Log file name.");
|
||||
data("logging.level", "java.util.Map<java.lang.String,java.lang.Object>", null, "Log levels severity mapping. Use 'root' for the root logger.");
|
||||
data("logging.path", "java.lang.String", null, "Location of the log file.");
|
||||
data("multipart.file-size-threshold", "java.lang.String", "0", "Threshold after which files will be written to disk. Values can use the suffixed\n \"MB\" or \"KB\" to indicate a Megabyte or Kilobyte size.");
|
||||
data("multipart.location", "java.lang.String", null, "Intermediate location of uploaded files.");
|
||||
data("multipart.max-file-size", "java.lang.String", "1Mb", "Max file size. Values can use the suffixed \"MB\" or \"KB\" to indicate a Megabyte or\n Kilobyte size.");
|
||||
data("multipart.max-request-size", "java.lang.String", "10Mb", "Max request size. Values can use the suffixed \"MB\" or \"KB\" to indicate a Megabyte\n or Kilobyte size.");
|
||||
data("security.basic.enabled", "java.lang.Boolean", "true", "Enable basic authentication.");
|
||||
data("security.basic.path", "java.lang.String[]", "[Ljava.lang.Object;@7abd0056", "Comma-separated list of paths to secure.");
|
||||
data("security.basic.realm", "java.lang.String", "Spring", "HTTP basic realm name.");
|
||||
data("security.enable-csrf", "java.lang.Boolean", "false", "Enable Cross Site Request Forgery support.");
|
||||
data("security.filter-order", "java.lang.Integer", "0", "Security filter chain order.");
|
||||
data("security.headers.cache", "java.lang.Boolean", "false", "Enable cache control HTTP headers.");
|
||||
data("security.headers.content-type", "java.lang.Boolean", "false", "Enable \"X-Content-Type-Options\" header.");
|
||||
data("security.headers.frame", "java.lang.Boolean", "false", "Enable \"X-Frame-Options\" header.");
|
||||
data("security.headers.hsts", "org.springframework.boot.autoconfigure.security.SecurityProperties$Headers$HSTS", null, "HTTP Strict Transport Security (HSTS) mode (none, domain, all).");
|
||||
data("security.headers.xss", "java.lang.Boolean", "false", "Enable cross site scripting (XSS) protection.");
|
||||
data("security.ignored", "java.util.List<java.lang.String>", null, "Comma-separated list of paths to exclude from the default secured paths.");
|
||||
data("security.require-ssl", "java.lang.Boolean", "false", "Enable secure channel for all requests.");
|
||||
data("security.sessions", "org.springframework.security.config.http.SessionCreationPolicy", null, "Session creation policy (always, never, if_required, stateless).");
|
||||
data("security.user.name", "java.lang.String", "user", "Default user name.");
|
||||
data("security.user.password", "java.lang.String", null, "Password for the default user name.");
|
||||
data("security.user.role", "java.util.List<java.lang.String>", null, "Granted roles for the default user name.");
|
||||
data("server.address", "java.net.InetAddress", null, "Network address to which the server should bind to.");
|
||||
data("server.context-parameters", "java.util.Map<java.lang.String,java.lang.String>", null, "ServletContext parameters.");
|
||||
data("server.context-path", "java.lang.String", null, "Context path of the application.");
|
||||
data("server.port", "java.lang.Integer", null, "Server HTTP port.");
|
||||
data("server.servlet-path", "java.lang.String", "/", "Path of the main dispatcher servlet.");
|
||||
data("server.session-timeout", "java.lang.Integer", null, "Session timeout in seconds.");
|
||||
data("server.ssl.ciphers", "java.lang.String[]", null, null);
|
||||
data("server.ssl.client-auth", "org.springframework.boot.context.embedded.Ssl$ClientAuth", null, null);
|
||||
data("server.ssl.key-alias", "java.lang.String", null, null);
|
||||
data("server.ssl.key-password", "java.lang.String", null, null);
|
||||
data("server.ssl.key-store", "java.lang.String", null, null);
|
||||
data("server.ssl.key-store-password", "java.lang.String", null, null);
|
||||
data("server.ssl.key-store-provider", "java.lang.String", null, null);
|
||||
data("server.ssl.key-store-type", "java.lang.String", null, null);
|
||||
data("server.ssl.protocol", "java.lang.String", null, null);
|
||||
data("server.ssl.trust-store", "java.lang.String", null, null);
|
||||
data("server.ssl.trust-store-password", "java.lang.String", null, null);
|
||||
data("server.ssl.trust-store-provider", "java.lang.String", null, null);
|
||||
data("server.ssl.trust-store-type", "java.lang.String", null, null);
|
||||
data("server.tomcat.access-log-enabled", "java.lang.Boolean", "false", "Enable access log.");
|
||||
data("server.tomcat.access-log-pattern", "java.lang.String", null, "Format pattern for access logs.");
|
||||
data("server.tomcat.background-processor-delay", "java.lang.Integer", "30", "Delay in seconds between the invocation of backgroundProcess methods.");
|
||||
data("server.tomcat.basedir", "java.io.File", null, "Tomcat base directory. If not specified a temporary directory will be used.");
|
||||
data("server.tomcat.internal-proxies", "java.lang.String", "10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|192\\.168\\.\\d{1,3}\\.\\d{1,3}|169\\.254\\.\\d{1,3}\\.\\d{1,3}|127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}", "Regular expression that matches proxies that are to be trusted.");
|
||||
data("server.tomcat.max-http-header-size", "java.lang.Integer", "0", "Maximum size in bytes of the HTTP message header.");
|
||||
data("server.tomcat.max-threads", "java.lang.Integer", "0", "Maximum amount of worker threads.");
|
||||
data("server.tomcat.port-header", "java.lang.String", null, "Name of the HTTP header used to override the original port value.");
|
||||
data("server.tomcat.protocol-header", "java.lang.String", null, "Header that holds the incoming protocol, usually named \"X-Forwarded-Proto\".\n Configured as a RemoteIpValve only if remoteIpHeader is also set.");
|
||||
data("server.tomcat.remote-ip-header", "java.lang.String", null, "Name of the http header from which the remote ip is extracted. Configured as a\n RemoteIpValve only if remoteIpHeader is also set.");
|
||||
data("server.tomcat.uri-encoding", "java.lang.String", null, "Character encoding to use to decode the URI.");
|
||||
data("server.undertow.buffer-size", "java.lang.Integer", null, "Size of each buffer in bytes.");
|
||||
data("server.undertow.buffers-per-region", "java.lang.Integer", null, "Number of buffer per region.");
|
||||
data("server.undertow.direct-buffers", "java.lang.Boolean", null, null);
|
||||
data("server.undertow.io-threads", "java.lang.Integer", null, "Number of I/O threads to create for the worker.");
|
||||
data("server.undertow.worker-threads", "java.lang.Integer", null, "Number of worker threads.");
|
||||
data("spring.activemq.broker-url", "java.lang.String", null, "URL of the ActiveMQ broker. Auto-generated by default.");
|
||||
data("spring.activemq.in-memory", "java.lang.Boolean", "true", "Specify if the default broker URL should be in memory. Ignored if an explicit\n broker has been specified.");
|
||||
data("spring.activemq.password", "java.lang.String", null, "Login password of the broker.");
|
||||
data("spring.activemq.pooled", "java.lang.Boolean", "false", "Specify if a PooledConnectionFactory should be created instead of a regular\n ConnectionFactory.");
|
||||
data("spring.activemq.user", "java.lang.String", null, "Login user of the broker.");
|
||||
data("spring.aop.auto", "java.lang.Boolean", "true", "Add @EnableAspectJAutoProxy.");
|
||||
data("spring.aop.proxy-target-class", "java.lang.Boolean", "false", "Whether subclass-based (CGLIB) proxies are to be created (true) as opposed to standard Java interface-based proxies (false).");
|
||||
data("spring.application.index", "java.lang.Integer", null, "Application index.");
|
||||
data("spring.application.name", "java.lang.String", null, "Application name.");
|
||||
data("spring.batch.initializer.enabled", "java.lang.Boolean", "true", "Create the required batch tables on startup if necessary.");
|
||||
data("spring.batch.job.enabled", "java.lang.Boolean", "true", "Execute all Spring Batch jobs in the context on startup.");
|
||||
data("spring.batch.job.names", "java.lang.String", "", "Comma-separated list of job names to execute on startup. By default, all Jobs\n found in the context are executed.");
|
||||
data("spring.batch.schema", "java.lang.String", "classpath:org/springframework/batch/core/schema-@@platform@@.sql", "Path to the SQL file to use to initialize the database schema.");
|
||||
data("spring.config.location", "java.lang.String", null, "Config file locations.");
|
||||
data("spring.config.name", "java.lang.String", "application", "Config file name.");
|
||||
data("spring.dao.exceptiontranslation.enabled", "java.lang.Boolean", "true", "Enable the PersistenceExceptionTranslationPostProcessor.");
|
||||
data("spring.data.elasticsearch.cluster-name", "java.lang.String", "elasticsearch", "Elasticsearch cluster name.");
|
||||
data("spring.data.elasticsearch.cluster-nodes", "java.lang.String", null, "Comma-separated list of cluster node addresses. If not specified, starts a client\n node.");
|
||||
data("spring.data.elasticsearch.repositories.enabled", "java.lang.Boolean", "true", "Enable Elasticsearch repositories.");
|
||||
data("spring.data.jpa.repositories.enabled", "java.lang.Boolean", "true", "Enable JPA repositories.");
|
||||
data("spring.data.mongodb.authentication-database", "java.lang.String", null, "Authentication database name.");
|
||||
data("spring.data.mongodb.database", "java.lang.String", null, "Database name.");
|
||||
data("spring.data.mongodb.grid-fs-database", "java.lang.String", null, "GridFS database name.");
|
||||
data("spring.data.mongodb.host", "java.lang.String", null, "Mongo server host.");
|
||||
data("spring.data.mongodb.password", "char[]", null, "Login password of the mongo server.");
|
||||
data("spring.data.mongodb.port", "java.lang.Integer", null, "Mongo server port.");
|
||||
data("spring.data.mongodb.repositories.enabled", "java.lang.Boolean", "true", "Enable Mongo repositories.");
|
||||
data("spring.data.mongodb.uri", "java.lang.String", "mongodb://localhost/test", "Mmongo database URI. When set, host and port are ignored.");
|
||||
data("spring.data.mongodb.username", "java.lang.String", null, "Login user of the mongo server.");
|
||||
data("spring.data.rest.base-uri", "java.net.URI", null, null);
|
||||
data("spring.data.rest.default-page-size", "java.lang.Integer", null, null);
|
||||
data("spring.data.rest.limit-param-name", "java.lang.String", null, null);
|
||||
data("spring.data.rest.max-page-size", "java.lang.Integer", null, null);
|
||||
data("spring.data.rest.page-param-name", "java.lang.String", null, null);
|
||||
data("spring.data.rest.return-body-on-create", "java.lang.Boolean", null, null);
|
||||
data("spring.data.rest.return-body-on-update", "java.lang.Boolean", null, null);
|
||||
data("spring.data.rest.sort-param-name", "java.lang.String", null, null);
|
||||
data("spring.data.solr.host", "java.lang.String", "http://127.0.0.1:8983/solr", "Solr host. Ignored if \"zk-host\" is set.");
|
||||
data("spring.data.solr.repositories.enabled", "java.lang.Boolean", "true", "Enable Solr repositories.");
|
||||
data("spring.data.solr.zk-host", "java.lang.String", null, "ZooKeeper host address in the form HOST:PORT.");
|
||||
data("spring.datasource.abandon-when-percentage-full", "java.lang.Integer", null, null);
|
||||
data("spring.datasource.access-to-underlying-connection-allowed", "java.lang.Boolean", null, null);
|
||||
data("spring.datasource.alternate-username-allowed", "java.lang.Boolean", null, null);
|
||||
data("spring.datasource.auto-commit", "java.lang.Boolean", null, null);
|
||||
data("spring.datasource.catalog", "java.lang.String", null, null);
|
||||
data("spring.datasource.commit-on-return", "java.lang.Boolean", null, null);
|
||||
data("spring.datasource.connection-customizer-class-name", "java.lang.String", null, null);
|
||||
data("spring.datasource.connection-init-sql", "java.lang.String", null, null);
|
||||
data("spring.datasource.connection-init-sqls", "java.util.Collection", null, null);
|
||||
data("spring.datasource.connection-properties", "java.lang.String", null, null);
|
||||
data("spring.datasource.connection-test-query", "java.lang.String", null, null);
|
||||
data("spring.datasource.connection-timeout", "java.lang.Long", null, null);
|
||||
data("spring.datasource.continue-on-error", "java.lang.Boolean", "false", "Do not stop if an error occurs while initializing the database.");
|
||||
data("spring.datasource.data", "java.lang.String", null, "Data (DML) script resource reference.");
|
||||
data("spring.datasource.data-source-class-name", "java.lang.String", null, null);
|
||||
data("spring.datasource.data-source", "java.lang.Object", null, null);
|
||||
data("spring.datasource.data-source-j-n-d-i", "java.lang.String", null, null);
|
||||
data("spring.datasource.data-source-properties", "java.util.Properties", null, null);
|
||||
data("spring.datasource.db-properties", "java.util.Properties", null, null);
|
||||
data("spring.datasource.default-auto-commit", "java.lang.Boolean", null, null);
|
||||
data("spring.datasource.default-catalog", "java.lang.String", null, null);
|
||||
data("spring.datasource.default-read-only", "java.lang.Boolean", null, null);
|
||||
data("spring.datasource.default-transaction-isolation", "java.lang.Integer", null, null);
|
||||
data("spring.datasource.driver-class-name", "java.lang.String", null, "Fully qualified name of the JDBC driver. Auto-detected based on the URL by default.");
|
||||
data("spring.datasource.fair-queue", "java.lang.Boolean", null, null);
|
||||
data("spring.datasource.idle-timeout", "java.lang.Long", null, null);
|
||||
data("spring.datasource.ignore-exception-on-pre-load", "java.lang.Boolean", null, null);
|
||||
data("spring.datasource.initialization-fail-fast", "java.lang.Boolean", null, null);
|
||||
data("spring.datasource.initialize", "java.lang.Boolean", "true", "Populate the database using 'data.sql'.");
|
||||
data("spring.datasource.initial-size", "java.lang.Integer", null, null);
|
||||
data("spring.datasource.init-s-q-l", "java.lang.String", null, null);
|
||||
data("spring.datasource.isolate-internal-queries", "java.lang.Boolean", null, null);
|
||||
data("spring.datasource.jdbc4-connection-test", "java.lang.Boolean", null, null);
|
||||
data("spring.datasource.jdbc-interceptors", "java.lang.String", null, null);
|
||||
data("spring.datasource.jdbc-url", "java.lang.String", null, null);
|
||||
data("spring.datasource.jmx-enabled", "java.lang.Boolean", "false", "Enable JMX support (if provided by the underlying pool).");
|
||||
data("spring.datasource.jndi-name", "java.lang.String", null, "JNDI location of the datasource. Class, url, username & password are ignored when\n set.");
|
||||
data("spring.datasource.leak-detection-threshold", "java.lang.Long", null, null);
|
||||
data("spring.datasource.log-abandoned", "java.lang.Boolean", null, null);
|
||||
data("spring.datasource.login-timeout", "java.lang.Integer", null, null);
|
||||
data("spring.datasource.log-validation-errors", "java.lang.Boolean", null, null);
|
||||
data("spring.datasource.max-active", "java.lang.Integer", null, null);
|
||||
data("spring.datasource.max-age", "java.lang.Long", null, null);
|
||||
data("spring.datasource.max-idle", "java.lang.Integer", null, null);
|
||||
data("spring.datasource.maximum-pool-size", "java.lang.Integer", null, null);
|
||||
data("spring.datasource.max-lifetime", "java.lang.Long", null, null);
|
||||
data("spring.datasource.max-open-prepared-statements", "java.lang.Integer", null, null);
|
||||
data("spring.datasource.max-wait", "java.lang.Integer", null, null);
|
||||
data("spring.datasource.metric-registry", "java.lang.Object", null, null);
|
||||
data("spring.datasource.min-evictable-idle-time-millis", "java.lang.Integer", null, null);
|
||||
data("spring.datasource.min-idle", "java.lang.Integer", null, null);
|
||||
data("spring.datasource.minimum-idle", "java.lang.Integer", null, null);
|
||||
data("spring.datasource.name", "java.lang.String", null, null);
|
||||
data("spring.datasource.num-tests-per-eviction-run", "java.lang.Integer", null, null);
|
||||
data("spring.datasource.password", "java.lang.String", null, "Login password of the database.");
|
||||
data("spring.datasource.platform", "java.lang.String", "all", "Platform to use in the schema resource (schema-${platform}.sql).");
|
||||
data("spring.datasource.pool-name", "java.lang.String", null, null);
|
||||
data("spring.datasource.pool-prepared-statements", "java.lang.Boolean", null, null);
|
||||
data("spring.datasource.propagate-interrupt-state", "java.lang.Boolean", null, null);
|
||||
data("spring.datasource.read-only", "java.lang.Boolean", null, null);
|
||||
data("spring.datasource.register-mbeans", "java.lang.Boolean", null, null);
|
||||
data("spring.datasource.remove-abandoned", "java.lang.Boolean", null, null);
|
||||
data("spring.datasource.remove-abandoned-timeout", "java.lang.Integer", null, null);
|
||||
data("spring.datasource.rollback-on-return", "java.lang.Boolean", null, null);
|
||||
data("spring.datasource.schema", "java.lang.String", null, "Schema (DDL) script resource reference.");
|
||||
data("spring.datasource.separator", "java.lang.String", ";", "Statement separator in SQL initialization scripts.");
|
||||
data("spring.datasource.sql-script-encoding", "java.lang.String", null, "SQL scripts encoding.");
|
||||
data("spring.datasource.suspect-timeout", "java.lang.Integer", null, null);
|
||||
data("spring.datasource.test-on-borrow", "java.lang.Boolean", null, null);
|
||||
data("spring.datasource.test-on-connect", "java.lang.Boolean", null, null);
|
||||
data("spring.datasource.test-on-return", "java.lang.Boolean", null, null);
|
||||
data("spring.datasource.test-while-idle", "java.lang.Boolean", null, null);
|
||||
data("spring.datasource.time-between-eviction-runs-millis", "java.lang.Integer", null, null);
|
||||
data("spring.datasource.transaction-isolation", "java.lang.String", null, null);
|
||||
data("spring.datasource.url", "java.lang.String", null, "JDBC url of the database.");
|
||||
data("spring.datasource.use-disposable-connection-facade", "java.lang.Boolean", null, null);
|
||||
data("spring.datasource.use-equals", "java.lang.Boolean", null, null);
|
||||
data("spring.datasource.use-lock", "java.lang.Boolean", null, null);
|
||||
data("spring.datasource.username", "java.lang.String", null, "Login user of the database.");
|
||||
data("spring.datasource.validation-interval", "java.lang.Long", null, null);
|
||||
data("spring.datasource.validation-query", "java.lang.String", null, null);
|
||||
data("spring.datasource.validation-query-timeout", "java.lang.Integer", null, null);
|
||||
data("spring.datasource.validator-class-name", "java.lang.String", null, null);
|
||||
data("spring.datasource.xa.data-source-class-name", "java.lang.String", null, "XA datasource fully qualified name.");
|
||||
data("spring.datasource.xa.properties", "java.util.Map<java.lang.String,java.lang.String>", null, "Properties to pass to the XA data source.");
|
||||
data("spring.freemarker.allow-request-override", "java.lang.Boolean", null, "Set whether HttpServletRequest attributes are allowed to override (hide) controller\n generated model attributes of the same name.");
|
||||
data("spring.freemarker.cache", "java.lang.Boolean", null, "Enable template caching.");
|
||||
data("spring.freemarker.char-set", "java.lang.String", null, null);
|
||||
data("spring.freemarker.charset", "java.lang.String", null, "Template encoding.");
|
||||
data("spring.freemarker.check-template-location", "java.lang.Boolean", null, "Check that the templates location exists.");
|
||||
data("spring.freemarker.content-type", "java.lang.String", null, "Content-Type value.");
|
||||
data("spring.freemarker.enabled", "java.lang.Boolean", null, "Enable MVC view resolution for this technology.");
|
||||
data("spring.freemarker.expose-request-attributes", "java.lang.Boolean", null, "Set whether all request attributes should be added to the model prior to merging\n with the template.");
|
||||
data("spring.freemarker.expose-session-attributes", "java.lang.Boolean", null, "Set whether all HttpSession attributes should be added to the model prior to\n merging with the template.");
|
||||
data("spring.freemarker.expose-spring-macro-helpers", "java.lang.Boolean", null, "Set whether to expose a RequestContext for use by Spring's macro library, under the\n name \"springMacroRequestContext\".");
|
||||
data("spring.freemarker.prefix", "java.lang.String", null, "Prefix that gets prepended to view names when building a URL.");
|
||||
data("spring.freemarker.request-context-attribute", "java.lang.String", null, "Name of the RequestContext attribute for all views.");
|
||||
data("spring.freemarker.settings", "java.util.Map<java.lang.String,java.lang.String>", null, "Well-known FreeMarker keys which will be passed to FreeMarker's Configuration.");
|
||||
data("spring.freemarker.suffix", "java.lang.String", null, "Suffix that gets appended to view names when building a URL.");
|
||||
data("spring.freemarker.template-loader-path", "java.lang.String[]", new String[] {"snuzzle" ,"buggles"}, "Comma-separated list of template paths.");
|
||||
data("spring.freemarker.view-names", "java.lang.String[]", null, "White list of view names that can be resolved.");
|
||||
data("spring.groovy.template.cache", "java.lang.Boolean", null, "Enable template caching.");
|
||||
data("spring.groovy.template.char-set", "java.lang.String", null, null);
|
||||
data("spring.groovy.template.charset", "java.lang.String", null, "Template encoding.");
|
||||
data("spring.groovy.template.check-template-location", "java.lang.Boolean", null, "Check that the templates location exists.");
|
||||
data("spring.groovy.template.configuration.auto-escape", "java.lang.Boolean", null, null);
|
||||
data("spring.groovy.template.configuration.auto-indent", "java.lang.Boolean", null, null);
|
||||
data("spring.groovy.template.configuration.auto-indent-string", "java.lang.String", null, null);
|
||||
data("spring.groovy.template.configuration.auto-new-line", "java.lang.Boolean", null, null);
|
||||
data("spring.groovy.template.configuration.base-template-class", "java.lang.Class<? extends groovy.text.markup.BaseTemplate>", null, null);
|
||||
data("spring.groovy.template.configuration.cache-templates", "java.lang.Boolean", null, null);
|
||||
data("spring.groovy.template.configuration.declaration-encoding", "java.lang.String", null, null);
|
||||
data("spring.groovy.template.configuration.expand-empty-elements", "java.lang.Boolean", null, null);
|
||||
data("spring.groovy.template.configuration", "java.util.Map<java.lang.String,java.lang.Object>", null, "Configuration to pass to TemplateConfiguration.");
|
||||
data("spring.groovy.template.configuration.locale", "java.util.Locale", null, null);
|
||||
data("spring.groovy.template.configuration.new-line-string", "java.lang.String", null, null);
|
||||
data("spring.groovy.template.configuration.resource-loader-path", "java.lang.String", null, null);
|
||||
data("spring.groovy.template.configuration.use-double-quotes", "java.lang.Boolean", null, null);
|
||||
data("spring.groovy.template.content-type", "java.lang.String", null, "Content-Type value.");
|
||||
data("spring.groovy.template.enabled", "java.lang.Boolean", null, "Enable MVC view resolution for this technology.");
|
||||
data("spring.groovy.template.prefix", "java.lang.String", "classpath:/templates/", "Prefix that gets prepended to view names when building a URL.");
|
||||
data("spring.groovy.template.suffix", "java.lang.String", ".tpl", "Suffix that gets appended to view names when building a URL.");
|
||||
data("spring.groovy.template.view-names", "java.lang.String[]", null, "White list of view names that can be resolved.");
|
||||
data("spring.hornetq.embedded.cluster-password", "java.lang.String", null, "Cluster password. Randomly generated on startup by default");
|
||||
data("spring.hornetq.embedded.data-directory", "java.lang.String", null, "Journal file directory. Not necessary if persistence is turned off.");
|
||||
data("spring.hornetq.embedded.enabled", "java.lang.Boolean", "true", "Enable embedded mode if the HornetQ server APIs are available.");
|
||||
data("spring.hornetq.embedded.persistent", "java.lang.Boolean", "false", "Enable persistent store.");
|
||||
data("spring.hornetq.embedded.queues", "java.lang.String[]", "[Ljava.lang.Object;@2f5ce114", "Comma-separate list of queues to create on startup.");
|
||||
data("spring.hornetq.embedded.server-id", "java.lang.Integer", "0", "Server id. By default, an auto-incremented counter is used.");
|
||||
data("spring.hornetq.embedded.topics", "java.lang.String[]", "[Ljava.lang.Object;@6272137a", "Comma-separate list of topics to create on startup.");
|
||||
data("spring.hornetq.host", "java.lang.String", "localhost", "HornetQ broker host.");
|
||||
data("spring.hornetq.mode", "org.springframework.boot.autoconfigure.jms.hornetq.HornetQMode", null, "HornetQ deployment mode, auto-detected by default. Can be explicitly set to\n \"native\" or \"embedded\".");
|
||||
data("spring.hornetq.port", "java.lang.Integer", "5445", "HornetQ broker port.");
|
||||
data("spring.http.encoding.charset", "java.nio.charset.Charset", null, "Charset of HTTP requests and responses. Added to the \"Content-Type\" header if not\n set explicitly.");
|
||||
data("spring.http.encoding.enabled", "java.lang.Boolean", "true", "Enable http encoding support.");
|
||||
data("spring.http.encoding.force", "java.lang.Boolean", "true", "Force the encoding to the configured charset on HTTP requests and responses.");
|
||||
data("spring.jackson.date-format", "java.lang.String", null, "Date format string (yyyy-MM-dd HH:mm:ss), or a fully-qualified date format class\n name.");
|
||||
data("spring.jackson.deserialization", "java.util.Map<com.fasterxml.jackson.databind.DeserializationFeature,java.lang.Boolean>", null, "Jackson on/off features that affect the way Java objects are deserialized.");
|
||||
data("spring.jackson.generator", "java.util.Map<com.fasterxml.jackson.core.JsonGenerator.Feature,java.lang.Boolean>", null, "Jackson on/off features for generators.");
|
||||
data("spring.jackson.mapper", "java.util.Map<com.fasterxml.jackson.databind.MapperFeature,java.lang.Boolean>", null, "Jackson general purpose on/off features.");
|
||||
data("spring.jackson.parser", "java.util.Map<com.fasterxml.jackson.core.JsonParser.Feature,java.lang.Boolean>", null, "Jackson on/off features for parsers.");
|
||||
data("spring.jackson.property-naming-strategy", "java.lang.String", null, "One of the constants on Jackson's PropertyNamingStrategy\n (CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES). Can also be a fully-qualified class\n name of a PropertyNamingStrategy subclass.");
|
||||
data("spring.jackson.serialization", "java.util.Map<com.fasterxml.jackson.databind.SerializationFeature,java.lang.Boolean>", null, "Jackson on/off features that affect the way Java objects are serialized.");
|
||||
data("spring.jersey.filter.order", "java.lang.Integer", "0", "Jersey filter chain order.");
|
||||
data("spring.jersey.init", "java.util.Map<java.lang.String,java.lang.String>", null, "Init parameters to pass to Jersey.");
|
||||
data("spring.jersey.type", "org.springframework.boot.autoconfigure.jersey.JerseyProperties$Type", null, "Jersey integration type. Can be either \"servlet\" or \"filter\".");
|
||||
data("spring.jms.jndi-name", "java.lang.String", null, "Connection factory JNDI name. When set, takes precedence to others connection\n factory auto-configurations.");
|
||||
data("spring.jms.pub-sub-domain", "java.lang.Boolean", "false", "Specify if the default destination type is topic.");
|
||||
data("spring.jmx.enabled", "java.lang.Boolean", "true", "Expose management beans to the JMX domain.");
|
||||
data("spring.jpa.database", "org.springframework.orm.jpa.vendor.Database", null, "Target database to operate on, auto-detected by default. Can be alternatively set\n using the \"databasePlatform\" property.");
|
||||
data("spring.jpa.database-platform", "java.lang.String", null, "Name of the target database to operate on, auto-detected by default. Can be\n alternatively set using the \"Database\" enum.");
|
||||
data("spring.jpa.generate-ddl", "java.lang.Boolean", "false", "Initialize the schema on startup.");
|
||||
data("spring.jpa.hibernate.ddl-auto", "java.lang.String", null, "DDL mode (\"none\", \"validate\", \"update\", \"create\", \"create-drop\"). This is\n actually a shortcut for the \"hibernate.hbm2ddl.auto\" property. Default to\n \"create-drop\" when using an embedded database, \"none\" otherwise.");
|
||||
data("spring.jpa.hibernate.naming-strategy", "java.lang.Class<?>", null, "Naming strategy fully qualified name.");
|
||||
data("spring.jpa.open-in-view", "java.lang.Boolean", "true", "Register OpenEntityManagerInViewInterceptor. Binds a JPA EntityManager to the thread for the entire processing of the request.");
|
||||
data("spring.jpa.properties", "java.util.Map<java.lang.String,java.lang.String>", null, "Additional native properties to set on the JPA provider.");
|
||||
data("spring.jpa.show-sql", "java.lang.Boolean", "false", "Enable logging of SQL statements.");
|
||||
data("spring.jta.allow-multiple-lrc", "java.lang.Boolean", null, null);
|
||||
data("spring.jta.asynchronous2-pc", "java.lang.Boolean", null, null);
|
||||
data("spring.jta.background-recovery-interval", "java.lang.Integer", null, null);
|
||||
data("spring.jta.background-recovery-interval-seconds", "java.lang.Integer", null, null);
|
||||
data("spring.jta.current-node-only-recovery", "java.lang.Boolean", null, null);
|
||||
data("spring.jta.debug-zero-resource-transaction", "java.lang.Boolean", null, null);
|
||||
data("spring.jta.default-transaction-timeout", "java.lang.Integer", null, null);
|
||||
data("spring.jta.disable-jmx", "java.lang.Boolean", null, null);
|
||||
data("spring.jta.enabled", "java.lang.Boolean", "true", "Enable JTA support.");
|
||||
data("spring.jta.exception-analyzer", "java.lang.String", null, null);
|
||||
data("spring.jta.filter-log-status", "java.lang.Boolean", null, null);
|
||||
data("spring.jta.force-batching-enabled", "java.lang.Boolean", null, null);
|
||||
data("spring.jta.forced-write-enabled", "java.lang.Boolean", null, null);
|
||||
data("spring.jta.graceful-shutdown-interval", "java.lang.Integer", null, null);
|
||||
data("spring.jta.jndi-transaction-synchronization-registry-name", "java.lang.String", null, null);
|
||||
data("spring.jta.jndi-user-transaction-name", "java.lang.String", null, null);
|
||||
data("spring.jta.journal", "java.lang.String", null, null);
|
||||
data("spring.jta.log-dir", "java.lang.String", null, "Transaction logs directory.");
|
||||
data("spring.jta.log-part1-filename", "java.lang.String", null, null);
|
||||
data("spring.jta.log-part2-filename", "java.lang.String", null, null);
|
||||
data("spring.jta.max-log-size-in-mb", "java.lang.Integer", null, null);
|
||||
data("spring.jta.resource-configuration-filename", "java.lang.String", null, null);
|
||||
data("spring.jta.server-id", "java.lang.String", null, null);
|
||||
data("spring.jta.skip-corrupted-logs", "java.lang.Boolean", null, null);
|
||||
data("spring.jta.transaction-manager-id", "java.lang.String", null, "Transaction manager unique identifier.");
|
||||
data("spring.jta.warn-about-zero-resource-transaction", "java.lang.Boolean", null, null);
|
||||
data("spring.mail.default-encoding", "java.lang.String", "UTF-8", "Default MimeMessage encoding.");
|
||||
data("spring.mail.host", "java.lang.String", null, "SMTP server host.");
|
||||
data("spring.mail.password", "java.lang.String", null, "Login password of the SMTP server.");
|
||||
data("spring.mail.port", "java.lang.Integer", null, "SMTP server port.");
|
||||
data("spring.mail.properties", "java.util.Map<java.lang.String,java.lang.String>", null, "Additional JavaMail session properties.");
|
||||
data("spring.mail.username", "java.lang.String", null, "Login user of the SMTP server.");
|
||||
data("spring.main.show-banner", "java.lang.Boolean", "true", "Display the banner when the application runs.");
|
||||
data("spring.main.sources", "java.util.Set<java.lang.Object>", null, "Sources (class name, package name or XML resource location) used to create the ApplicationContext.");
|
||||
data("spring.main.web-environment", "java.lang.Boolean", null, "Run the application in a web environment (auto-detected by default).");
|
||||
data("spring.mandatory-file-encoding", "java.lang.String", null, "Expected character encoding the application must use.");
|
||||
data("spring.messages.basename", "java.lang.String", "messages", "Comma-separated list of basenames, each following the ResourceBundle convention.\n Essentially a fully-qualified classpath location. If it doesn't contain a package\n qualifier (such as \"org.mypackage\"), it will be resolved from the classpath root.");
|
||||
data("spring.messages.cache-seconds", "java.lang.Integer", "-1", "Loaded resource bundle files cache expiration, in seconds. When set to -1, bundles\n are cached forever.");
|
||||
data("spring.messages.encoding", "java.lang.String", "utf-8", "Message bundles encoding.");
|
||||
data("spring.mobile.devicedelegatingviewresolver.enabled", "java.lang.Boolean", "false", "Enable device view resolver.");
|
||||
data("spring.mobile.devicedelegatingviewresolver.mobile-prefix", "java.lang.String", "mobile/", "Prefix that gets prepended to view names for mobile devices.");
|
||||
data("spring.mobile.devicedelegatingviewresolver.mobile-suffix", "java.lang.String", "", "Suffix that gets appended to view names for mobile devices.");
|
||||
data("spring.mobile.devicedelegatingviewresolver.normal-prefix", "java.lang.String", "", "Prefix that gets prepended to view names for normal devices.");
|
||||
data("spring.mobile.devicedelegatingviewresolver.normal-suffix", "java.lang.String", "", "Suffix that gets appended to view names for normal devices.");
|
||||
data("spring.mobile.devicedelegatingviewresolver.tablet-prefix", "java.lang.String", "tablet/", "Prefix that gets prepended to view names for tablet devices.");
|
||||
data("spring.mobile.devicedelegatingviewresolver.tablet-suffix", "java.lang.String", "", "Suffix that gets appended to view names for tablet devices.");
|
||||
data("spring.mobile.sitepreference.enabled", "java.lang.Boolean", "true", "Enable SitePreferenceHandler.");
|
||||
data("spring.mvc.date-format", "java.lang.String", null, "Date format to use (e.g. dd/MM/yyyy)");
|
||||
data("spring.mvc.ignore-default-model-on-redirect", "java.lang.Boolean", "true", "If the the content of the \"default\" model should be ignored during redirect\n scenarios.");
|
||||
data("spring.mvc.locale", "java.lang.String", null, "Locale to use.");
|
||||
data("spring.mvc.message-codes-resolver-format", "org.springframework.validation.DefaultMessageCodesResolver$Format", null, "Formatting strategy for message codes (PREFIX_ERROR_CODE, POSTFIX_ERROR_CODE).");
|
||||
data("spring.profiles.active", "java.lang.String", null, "Comma-separated list of active profiles. Can be overridden by a command line switch.");
|
||||
data("spring.profiles.include", "java.lang.String", null, "Unconditionally activate the specified comma separated profiles.");
|
||||
data("spring.rabbitmq.addresses", "java.lang.String", null, "Comma-separated list of addresses to which the client should connect to.");
|
||||
data("spring.rabbitmq.dynamic", "java.lang.Boolean", "true", "Create an AmqpAdmin bean.");
|
||||
data("spring.rabbitmq.host", "java.lang.String", "localhost", "RabbitMQ host.");
|
||||
data("spring.rabbitmq.password", "java.lang.String", null, "Login to authenticate against the broker.");
|
||||
data("spring.rabbitmq.port", "java.lang.Integer", "5672", "RabbitMQ port.");
|
||||
data("spring.rabbitmq.username", "java.lang.String", null, "Login user to authenticate to the broker.");
|
||||
data("spring.rabbitmq.virtual-host", "java.lang.String", null, "Virtual host to use when connecting to the broker.");
|
||||
data("spring.redis.database", "java.lang.Integer", "0", "Database index used by the connection factory.");
|
||||
data("spring.redis.host", "java.lang.String", "localhost", "Redis server host.");
|
||||
data("spring.redis.password", "java.lang.String", null, "Login password of the redis server.");
|
||||
data("spring.redis.pool.max-active", "java.lang.Integer", "8", "Max number of connections that can be allocated by the pool at a given time.\n Use a negative value for no limit.");
|
||||
data("spring.redis.pool.max-idle", "java.lang.Integer", "8", "Max number of \"idle\" connections in the pool. Use a negative value to indicate\n an unlimited number of idle connections.");
|
||||
data("spring.redis.pool.max-wait", "java.lang.Integer", "-1", "Maximum amount of time (in milliseconds) a connection allocation should block\n before throwing an exception when the pool is exhausted. Use a negative value\n to block indefinitely.");
|
||||
data("spring.redis.pool.min-idle", "java.lang.Integer", "0", "Target for the minimum number of idle connections to maintain in the pool. This\n setting only has an effect if it is positive.");
|
||||
data("spring.redis.port", "java.lang.Integer", "6379", "Redis server port.");
|
||||
data("spring.redis.sentinel.master", "java.lang.String", null, "Name of Redis server.");
|
||||
data("spring.redis.sentinel.nodes", "java.lang.String", null, "Comma-separated list of host:port pairs.");
|
||||
data("spring.resources.add-mappings", "java.lang.Boolean", "true", "Enable default resource handling.");
|
||||
data("spring.resources.cache-period", "java.lang.Integer", null, "Cache period for the resources served by the resource handler, in seconds.");
|
||||
data("spring.social.auto-connection-views", "java.lang.Boolean", "false", "Enable the connection status view for supported providers.");
|
||||
data("spring.social.facebook.app-id", "java.lang.String", null, "Application id.");
|
||||
data("spring.social.facebook.app-secret", "java.lang.String", null, "Application secret.");
|
||||
data("spring.social.linkedin.app-id", "java.lang.String", null, "Application id.");
|
||||
data("spring.social.linkedin.app-secret", "java.lang.String", null, "Application secret.");
|
||||
data("spring.social.twitter.app-id", "java.lang.String", null, "Application id.");
|
||||
data("spring.social.twitter.app-secret", "java.lang.String", null, "Application secret.");
|
||||
data("spring.thymeleaf.cache", "java.lang.Boolean", "true", "Enable template caching.");
|
||||
data("spring.thymeleaf.check-template-location", "java.lang.Boolean", "true", "Check that the templates location exists.");
|
||||
data("spring.thymeleaf.content-type", "java.lang.String", "text/html", "Content-Type value.");
|
||||
data("spring.thymeleaf.enabled", "java.lang.Boolean", "true", "Enable MVC Thymeleaf view resolution.");
|
||||
data("spring.thymeleaf.encoding", "java.lang.String", "UTF-8", "Template encoding.");
|
||||
data("spring.thymeleaf.excluded-view-names", "java.lang.String[]", null, "Comma-separated list of view names that should be excluded from resolution.");
|
||||
data("spring.thymeleaf.mode", "java.lang.String", "HTML5", "Template mode to be applied to templates. See also StandardTemplateModeHandlers.");
|
||||
data("spring.thymeleaf.prefix", "java.lang.String", "classpath:/templates/", "Prefix that gets prepended to view names when building a URL.");
|
||||
data("spring.thymeleaf.suffix", "java.lang.String", ".html", "Suffix that gets appended to view names when building a URL.");
|
||||
data("spring.thymeleaf.view-names", "java.lang.String[]", null, "Comma-separated list of view names that can be resolved.");
|
||||
data("spring.velocity.allow-request-override", "java.lang.Boolean", null, "Set whether HttpServletRequest attributes are allowed to override (hide) controller\n generated model attributes of the same name.");
|
||||
data("spring.velocity.cache", "java.lang.Boolean", null, "Enable template caching.");
|
||||
data("spring.velocity.char-set", "java.lang.String", null, null);
|
||||
data("spring.velocity.charset", "java.lang.String", null, "Template encoding.");
|
||||
data("spring.velocity.check-template-location", "java.lang.Boolean", null, "Check that the templates location exists.");
|
||||
data("spring.velocity.content-type", "java.lang.String", null, "Content-Type value.");
|
||||
data("spring.velocity.date-tool-attribute", "java.lang.String", null, "Name of the DateTool helper object to expose in the Velocity context of the view.");
|
||||
data("spring.velocity.enabled", "java.lang.Boolean", null, "Enable MVC view resolution for this technology.");
|
||||
data("spring.velocity.expose-request-attributes", "java.lang.Boolean", null, "Set whether all request attributes should be added to the model prior to merging\n with the template.");
|
||||
data("spring.velocity.expose-session-attributes", "java.lang.Boolean", null, "Set whether all HttpSession attributes should be added to the model prior to\n merging with the template.");
|
||||
data("spring.velocity.expose-spring-macro-helpers", "java.lang.Boolean", null, "Set whether to expose a RequestContext for use by Spring's macro library, under the\n name \"springMacroRequestContext\".");
|
||||
data("spring.velocity.number-tool-attribute", "java.lang.String", null, "Name of the NumberTool helper object to expose in the Velocity context of the view.");
|
||||
data("spring.velocity.prefer-file-system-access", "java.lang.Boolean", "true", "Prefer file system access for template loading. File system access enables hot\n detection of template changes.");
|
||||
data("spring.velocity.prefix", "java.lang.String", null, "Prefix that gets prepended to view names when building a URL.");
|
||||
data("spring.velocity.properties", "java.util.Map<java.lang.String,java.lang.String>", null, "Additional velocity properties.");
|
||||
data("spring.velocity.request-context-attribute", "java.lang.String", null, "Name of the RequestContext attribute for all views.");
|
||||
data("spring.velocity.resource-loader-path", "java.lang.String", "classpath:/templates/", "Template path.");
|
||||
data("spring.velocity.suffix", "java.lang.String", null, "Suffix that gets appended to view names when building a URL.");
|
||||
data("spring.velocity.toolbox-config-location", "java.lang.String", null, "Velocity Toolbox config location, for example \"/WEB-INF/toolbox.xml\". Automatically\n loads a Velocity Tools toolbox definition file and expose all defined tools in the\n specified scopes.");
|
||||
data("spring.velocity.view-names", "java.lang.String[]", null, "White list of view names that can be resolved.");
|
||||
data("spring.view.prefix", "java.lang.String", null, "Spring MVC view prefix.");
|
||||
data("spring.view.suffix", "java.lang.String", null, "Spring MVC view suffix.");
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return datas == null || datas.isEmpty();
|
||||
}
|
||||
|
||||
public SpringPropertyIndexProvider getIndexProvider() {
|
||||
return indexProvider;
|
||||
}
|
||||
|
||||
public JavaProjectFinder getProjectFinder() {
|
||||
return (doc) -> Optional.ofNullable(testProject);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### NetBeans ###
|
||||
nbproject/private/
|
||||
build/
|
||||
nbbuild/
|
||||
dist/
|
||||
nbdist/
|
||||
.nb-gradle/
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.0/apache-maven-3.5.0-bin.zip
|
||||
@@ -1,225 +0,0 @@
|
||||
#!/bin/sh
|
||||
# ----------------------------------------------------------------------------
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Maven2 Start Up Batch script
|
||||
#
|
||||
# Required ENV vars:
|
||||
# ------------------
|
||||
# JAVA_HOME - location of a JDK home dir
|
||||
#
|
||||
# Optional ENV vars
|
||||
# -----------------
|
||||
# M2_HOME - location of maven2's installed home dir
|
||||
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||
# e.g. to debug Maven itself, use
|
||||
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
if [ -z "$MAVEN_SKIP_RC" ] ; then
|
||||
|
||||
if [ -f /etc/mavenrc ] ; then
|
||||
. /etc/mavenrc
|
||||
fi
|
||||
|
||||
if [ -f "$HOME/.mavenrc" ] ; then
|
||||
. "$HOME/.mavenrc"
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
# OS specific support. $var _must_ be set to either true or false.
|
||||
cygwin=false;
|
||||
darwin=false;
|
||||
mingw=false
|
||||
case "`uname`" in
|
||||
CYGWIN*) cygwin=true ;;
|
||||
MINGW*) mingw=true;;
|
||||
Darwin*) darwin=true
|
||||
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
|
||||
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
|
||||
if [ -z "$JAVA_HOME" ]; then
|
||||
if [ -x "/usr/libexec/java_home" ]; then
|
||||
export JAVA_HOME="`/usr/libexec/java_home`"
|
||||
else
|
||||
export JAVA_HOME="/Library/Java/Home"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
if [ -r /etc/gentoo-release ] ; then
|
||||
JAVA_HOME=`java-config --jre-home`
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$M2_HOME" ] ; then
|
||||
## resolve links - $0 may be a link to maven's home
|
||||
PRG="$0"
|
||||
|
||||
# need this for relative symlinks
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG="`dirname "$PRG"`/$link"
|
||||
fi
|
||||
done
|
||||
|
||||
saveddir=`pwd`
|
||||
|
||||
M2_HOME=`dirname "$PRG"`/..
|
||||
|
||||
# make it fully qualified
|
||||
M2_HOME=`cd "$M2_HOME" && pwd`
|
||||
|
||||
cd "$saveddir"
|
||||
# echo Using m2 at $M2_HOME
|
||||
fi
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched
|
||||
if $cygwin ; then
|
||||
[ -n "$M2_HOME" ] &&
|
||||
M2_HOME=`cygpath --unix "$M2_HOME"`
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||
[ -n "$CLASSPATH" ] &&
|
||||
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
|
||||
fi
|
||||
|
||||
# For Migwn, ensure paths are in UNIX format before anything is touched
|
||||
if $mingw ; then
|
||||
[ -n "$M2_HOME" ] &&
|
||||
M2_HOME="`(cd "$M2_HOME"; pwd)`"
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
|
||||
# TODO classpath?
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ]; then
|
||||
javaExecutable="`which javac`"
|
||||
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
|
||||
# readlink(1) is not available as standard on Solaris 10.
|
||||
readLink=`which readlink`
|
||||
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
|
||||
if $darwin ; then
|
||||
javaHome="`dirname \"$javaExecutable\"`"
|
||||
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
|
||||
else
|
||||
javaExecutable="`readlink -f \"$javaExecutable\"`"
|
||||
fi
|
||||
javaHome="`dirname \"$javaExecutable\"`"
|
||||
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
|
||||
JAVA_HOME="$javaHome"
|
||||
export JAVA_HOME
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$JAVACMD" ] ; then
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
else
|
||||
JAVACMD="`which java`"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
echo "Error: JAVA_HOME is not defined correctly." >&2
|
||||
echo " We cannot execute $JAVACMD" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
echo "Warning: JAVA_HOME environment variable is not set."
|
||||
fi
|
||||
|
||||
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
|
||||
|
||||
# traverses directory structure from process work directory to filesystem root
|
||||
# first directory with .mvn subdirectory is considered project base directory
|
||||
find_maven_basedir() {
|
||||
|
||||
if [ -z "$1" ]
|
||||
then
|
||||
echo "Path not specified to find_maven_basedir"
|
||||
return 1
|
||||
fi
|
||||
|
||||
basedir="$1"
|
||||
wdir="$1"
|
||||
while [ "$wdir" != '/' ] ; do
|
||||
if [ -d "$wdir"/.mvn ] ; then
|
||||
basedir=$wdir
|
||||
break
|
||||
fi
|
||||
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
|
||||
if [ -d "${wdir}" ]; then
|
||||
wdir=`cd "$wdir/.."; pwd`
|
||||
fi
|
||||
# end of workaround
|
||||
done
|
||||
echo "${basedir}"
|
||||
}
|
||||
|
||||
# concatenates all lines of a file
|
||||
concat_lines() {
|
||||
if [ -f "$1" ]; then
|
||||
echo "$(tr -s '\n' ' ' < "$1")"
|
||||
fi
|
||||
}
|
||||
|
||||
BASE_DIR=`find_maven_basedir "$(pwd)"`
|
||||
if [ -z "$BASE_DIR" ]; then
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
|
||||
echo $MAVEN_PROJECTBASEDIR
|
||||
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin; then
|
||||
[ -n "$M2_HOME" ] &&
|
||||
M2_HOME=`cygpath --path --windows "$M2_HOME"`
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
|
||||
[ -n "$CLASSPATH" ] &&
|
||||
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
|
||||
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
|
||||
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
|
||||
fi
|
||||
|
||||
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||
|
||||
exec "$JAVACMD" \
|
||||
$MAVEN_OPTS \
|
||||
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
|
||||
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
|
||||
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
|
||||
@@ -1,143 +0,0 @@
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||
@REM or more contributor license agreements. See the NOTICE file
|
||||
@REM distributed with this work for additional information
|
||||
@REM regarding copyright ownership. The ASF licenses this file
|
||||
@REM to you under the Apache License, Version 2.0 (the
|
||||
@REM "License"); you may not use this file except in compliance
|
||||
@REM with the License. You may obtain a copy of the License at
|
||||
@REM
|
||||
@REM http://www.apache.org/licenses/LICENSE-2.0
|
||||
@REM
|
||||
@REM Unless required by applicable law or agreed to in writing,
|
||||
@REM software distributed under the License is distributed on an
|
||||
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
@REM KIND, either express or implied. See the License for the
|
||||
@REM specific language governing permissions and limitations
|
||||
@REM under the License.
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Maven2 Start Up Batch script
|
||||
@REM
|
||||
@REM Required ENV vars:
|
||||
@REM JAVA_HOME - location of a JDK home dir
|
||||
@REM
|
||||
@REM Optional ENV vars
|
||||
@REM M2_HOME - location of maven2's installed home dir
|
||||
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
|
||||
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
|
||||
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||
@REM e.g. to debug Maven itself, use
|
||||
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
|
||||
@echo off
|
||||
@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
|
||||
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
|
||||
|
||||
@REM set %HOME% to equivalent of $HOME
|
||||
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
|
||||
|
||||
@REM Execute a user defined script before this one
|
||||
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
|
||||
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
|
||||
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
|
||||
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
|
||||
:skipRcPre
|
||||
|
||||
@setlocal
|
||||
|
||||
set ERROR_CODE=0
|
||||
|
||||
@REM To isolate internal variables from possible post scripts, we use another setlocal
|
||||
@setlocal
|
||||
|
||||
@REM ==== START VALIDATION ====
|
||||
if not "%JAVA_HOME%" == "" goto OkJHome
|
||||
|
||||
echo.
|
||||
echo Error: JAVA_HOME not found in your environment. >&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||
echo location of your Java installation. >&2
|
||||
echo.
|
||||
goto error
|
||||
|
||||
:OkJHome
|
||||
if exist "%JAVA_HOME%\bin\java.exe" goto init
|
||||
|
||||
echo.
|
||||
echo Error: JAVA_HOME is set to an invalid directory. >&2
|
||||
echo JAVA_HOME = "%JAVA_HOME%" >&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||
echo location of your Java installation. >&2
|
||||
echo.
|
||||
goto error
|
||||
|
||||
@REM ==== END VALIDATION ====
|
||||
|
||||
:init
|
||||
|
||||
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
|
||||
@REM Fallback to current working directory if not found.
|
||||
|
||||
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
|
||||
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
|
||||
|
||||
set EXEC_DIR=%CD%
|
||||
set WDIR=%EXEC_DIR%
|
||||
:findBaseDir
|
||||
IF EXIST "%WDIR%"\.mvn goto baseDirFound
|
||||
cd ..
|
||||
IF "%WDIR%"=="%CD%" goto baseDirNotFound
|
||||
set WDIR=%CD%
|
||||
goto findBaseDir
|
||||
|
||||
:baseDirFound
|
||||
set MAVEN_PROJECTBASEDIR=%WDIR%
|
||||
cd "%EXEC_DIR%"
|
||||
goto endDetectBaseDir
|
||||
|
||||
:baseDirNotFound
|
||||
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
|
||||
cd "%EXEC_DIR%"
|
||||
|
||||
:endDetectBaseDir
|
||||
|
||||
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
|
||||
|
||||
@setlocal EnableExtensions EnableDelayedExpansion
|
||||
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
|
||||
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
|
||||
|
||||
:endReadAdditionalConfig
|
||||
|
||||
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
|
||||
|
||||
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
|
||||
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||
|
||||
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
|
||||
if ERRORLEVEL 1 goto error
|
||||
goto end
|
||||
|
||||
:error
|
||||
set ERROR_CODE=1
|
||||
|
||||
:end
|
||||
@endlocal & set ERROR_CODE=%ERROR_CODE%
|
||||
|
||||
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
|
||||
@REM check for post script, once with legacy .bat ending and once with .cmd ending
|
||||
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
|
||||
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
|
||||
:skipRcPost
|
||||
|
||||
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
|
||||
if "%MAVEN_BATCH_PAUSE%" == "on" pause
|
||||
|
||||
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
|
||||
|
||||
exit /B %ERROR_CODE%
|
||||
@@ -1,54 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>empty-boot-15-web-app</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>empty-boot-15-web-app</name>
|
||||
<description>Demo project for Spring Boot</description>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>1.5.8.RELEASE</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<java.version>1.8</java.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
</project>
|
||||
@@ -1,12 +0,0 @@
|
||||
package com.example;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class EmptyBoot15WebAppApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(EmptyBoot15WebAppApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package com.example;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest
|
||||
public class EmptyBoot15WebAppApplicationTests {
|
||||
|
||||
@Test
|
||||
public void contextLoads() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### NetBeans ###
|
||||
nbproject/private/
|
||||
build/
|
||||
nbbuild/
|
||||
dist/
|
||||
nbdist/
|
||||
.nb-gradle/
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.2/apache-maven-3.5.2-bin.zip
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user