Move all non-conflicting source files from boot-java-language-server to

commons-boot
This commit is contained in:
Kris De Volder
2018-02-09 13:25:12 -08:00
parent 94dfa69954
commit fbcb77cac0
207 changed files with 75 additions and 327 deletions

View File

@@ -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

View File

@@ -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";
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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
);
};
}
}

View File

@@ -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;
});
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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()));
}
}

View File

@@ -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);
// }
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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());
}
}

View File

@@ -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];
}
}
}

View File

@@ -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) {
}
}

View File

@@ -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);
}
}

View File

@@ -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());
}
}
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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++);
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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 {
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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";
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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>

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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]));
// }
}

View File

@@ -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);
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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;
}
}

View File

@@ -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";
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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`]**"
);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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`]");
}
}

View File

@@ -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*/);
}
}

View File

@@ -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");
}
}

View File

@@ -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");
}
}

View File

@@ -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`]**"
);
}
}

View File

@@ -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`"
);
}
}

View File

@@ -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());
}
}

View File

@@ -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`]");
}
}

View File

@@ -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;
}
}

View File

@@ -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());
}
}

View File

@@ -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);
}
}

View File

@@ -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());
}
}

View File

@@ -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());
}
}

View File

@@ -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);
}
}

View File

@@ -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());
}
}

View File

@@ -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));
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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/

View File

@@ -1 +0,0 @@
distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.0/apache-maven-3.5.0-bin.zip

View File

@@ -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 "$@"

View File

@@ -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%

View File

@@ -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>

View File

@@ -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);
}
}

View File

@@ -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() {
}
}

View File

@@ -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/

View File

@@ -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