Add resource info to beans in injection reports

This commit is contained in:
Kris De Volder
2017-11-03 09:58:06 -07:00
parent 443b8ad62f
commit a70bc1a77f
15 changed files with 289 additions and 29 deletions

View File

@@ -28,6 +28,7 @@ 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;
@@ -73,7 +74,7 @@ public class AutowiredHoverProvider implements HoverProvider {
@Override
public CompletableFuture<Hover> provideHover(ASTNode node, Annotation annotation, ITypeBinding type, int offset,
TextDocument doc, SpringBootApp[] runningApps) {
TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) {
if (runningApps.length > 0) {
StringBuilder hover = new StringBuilder();
@@ -100,7 +101,7 @@ public class AutowiredHoverProvider implements HoverProvider {
for (LiveBean bean : relevantBeans) {
hover.append("\n\n");
hasAutowiring |= addAutomaticallyWired(hover, annotation, beans, bean);
hasAutowiring |= addAutomaticallyWired(hover, annotation, beans, bean, project);
}
}
}
@@ -137,7 +138,7 @@ public class AutowiredHoverProvider implements HoverProvider {
return null;
}
private boolean addAutomaticallyWired(StringBuilder hover, Annotation annotation, LiveBeansModel beans, LiveBean bean) {
private boolean addAutomaticallyWired(StringBuilder hover, Annotation annotation, LiveBeansModel beans, LiveBean bean, IJavaProject project) {
boolean result = false;
String[] dependencies = bean.getDependencies();
@@ -152,7 +153,7 @@ public class AutowiredHoverProvider implements HoverProvider {
}
List<LiveBean> dependencyBeans = beans.getBeansOfName(injectedBean);
for (LiveBean dependencyBean : dependencyBeans) {
hover.append("- " + LiveHoverUtils.showBean(dependencyBean));
hover.append("- " + LiveHoverUtils.showBeanWithResource(dependencyBean, " ", project));
}
firstDependency = false;
}

View File

@@ -32,6 +32,7 @@ import org.json.JSONObject;
import org.springframework.ide.vscode.boot.java.handlers.HoverProvider;
import org.springframework.ide.vscode.boot.java.utils.HoverContentUtils;
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.BadLocationException;
import org.springframework.ide.vscode.commons.util.Log;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
@@ -47,7 +48,7 @@ public class ConditionalsLiveHoverProvider implements HoverProvider {
@Override
public CompletableFuture<Hover> provideHover(ASTNode node, Annotation annotation, ITypeBinding type, int offset,
TextDocument doc, SpringBootApp[] runningApps) {
TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) {
return provideHover(annotation, doc, runningApps);
}

View File

@@ -185,7 +185,7 @@ public class BootJavaHoverProvider implements HoverHandler {
SpringBootApp[] runningApps = getRunningSpringApps(project);
if (runningApps.length>0) {
if (hasActuatorDependency(project)) {
return provider.provideHover(node, annotation, type, offset, doc, runningApps);
return provider.provideHover(node, annotation, type, offset, doc, project, runningApps);
} else {
DocumentRegion region = ASTUtils.nameRegion(doc, annotation);
if (region.containsOffset(offset)) {

View File

@@ -19,6 +19,7 @@ import org.eclipse.jdt.core.dom.ITypeBinding;
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;
/**
@@ -26,7 +27,7 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument;
*/
public interface HoverProvider {
CompletableFuture<Hover> provideHover(ASTNode node, Annotation annotation, ITypeBinding type, int offset, TextDocument doc, SpringBootApp[] runningApps);
CompletableFuture<Hover> provideHover(ASTNode node, Annotation annotation, ITypeBinding type, int offset, TextDocument doc, IJavaProject project, SpringBootApp[] runningApps);
Collection<Range> getLiveHoverHints(Annotation annotation, TextDocument doc, SpringBootApp[] runningApps);
}

View File

@@ -28,6 +28,7 @@ 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;
@@ -58,7 +59,7 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider
@Override
public CompletableFuture<Hover> provideHover(ASTNode node, Annotation annotation, ITypeBinding type, int offset,
TextDocument doc, SpringBootApp[] runningApps) {
TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) {
if (runningApps.length > 0) {
LiveBean definedBean = getDefinedBean(annotation);
@@ -80,7 +81,7 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider
hover.append(LiveHoverUtils.niceAppName(app) + ":");
for (LiveBean bean : relevantBeans) {
addInjectedInto(definedBean, hover, beans, bean);
addInjectedInto(definedBean, hover, beans, bean, project);
addAutomaticallyWiredContructor(hover, annotation, beans, bean);
}
}
@@ -103,7 +104,7 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider
//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) {
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()) {
@@ -115,7 +116,7 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider
if (!firstDependency) {
hover.append("\n");
}
hover.append("- " + LiveHoverUtils.showBean(dependingBean));
hover.append("- " + LiveHoverUtils.showBeanWithResource(dependingBean, " ", project));
firstDependency = false;
}
}

View File

@@ -10,7 +10,7 @@
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.livehover;
import static org.springframework.ide.vscode.boot.java.utils.ASTUtils.*;
import static org.springframework.ide.vscode.boot.java.utils.ASTUtils.nameRange;
import java.util.Collection;
import java.util.List;
@@ -29,6 +29,7 @@ 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;
@@ -47,7 +48,7 @@ public class ActiveProfilesProvider implements HoverProvider {
Annotation annotation,
ITypeBinding type,
int offset,
TextDocument doc, SpringBootApp[] runningApps
TextDocument doc, IJavaProject project, SpringBootApp[] runningApps
) {
if (runningApps.length>0) {
StringBuilder markdown = new StringBuilder();

View File

@@ -12,9 +12,12 @@ package org.springframework.ide.vscode.boot.java.livehover;
import java.util.stream.Stream;
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.StringUtil;
public class LiveHoverUtils {
@@ -28,6 +31,28 @@ public class LiveHoverUtils {
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
StringBuilder buf = new StringBuilder("Bean: ");
buf.append(bean.getId());
String type = bean.getType();
if (type!=null) {
buf.append(newline);
buf.append("Type: `"+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 "Process [PID=" + app.getProcessID() + ", name=`" + app.getProcessName() + "`]";
}

View File

@@ -31,6 +31,7 @@ import org.springframework.ide.vscode.boot.java.handlers.HoverProvider;
import org.springframework.ide.vscode.boot.java.utils.HoverContentUtils;
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;
@@ -49,7 +50,7 @@ public class RequestMappingHoverProvider implements HoverProvider {
@Override
public CompletableFuture<Hover> provideHover(ASTNode node, Annotation annotation,
ITypeBinding type, int offset, TextDocument doc, SpringBootApp[] runningApps) {
ITypeBinding type, int offset, TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) {
return provideHover(annotation, doc, runningApps);
}

View File

@@ -0,0 +1,74 @@
/*******************************************************************************
* 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.regex.Matcher;
import java.util.regex.Pattern;
import org.springframework.ide.vscode.commons.java.IClasspath;
import org.springframework.ide.vscode.commons.java.IJavaProject;
/**
* 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
}
switch (type) {
case FILE:
return "`"+projectRelativePath(path)+"`";
case CLASS_PATH_RESOURCE:
return "`"+path+"`";
default:
return path;
}
}
private String projectRelativePath(String pathStr) {
Path path = Paths.get(pathStr);
IClasspath classpath = project.getClasspath();
Path outputFolder = classpath.getOutputFolder();
System.out.println("outf = "+outputFolder);
System.out.println("path = "+pathStr);
if (path.startsWith(outputFolder)) {
return outputFolder.relativize(path).toString();
}
return pathStr;
}
}

View File

@@ -28,6 +28,7 @@ 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.commons.boot.app.cli.SpringBootApp;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
/**
@@ -37,7 +38,7 @@ public class ValueHoverProvider implements HoverProvider {
@Override
public CompletableFuture<Hover> provideHover(ASTNode node, Annotation annotation, ITypeBinding type, int offset,
TextDocument doc, SpringBootApp[] runningApps) {
TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) {
try {
// case: @Value("prefix<*>")

View File

@@ -127,8 +127,10 @@ public class AutowiredHoverProviderTest {
"\n" +
"Bean [id: autowiredClass, type: `com.example.AutowiredClass`] got autowired with:\n" +
"\n" +
"- Bean [id: dependencyA, type: `com.example.DependencyA`]\n" +
"- Bean [id: dependencyB, type: `com.example.DependencyB`]\n"
"- Bean: dependencyA \n" +
" Type: `com.example.DependencyA`\n" +
"- Bean: dependencyB \n" +
" Type: `com.example.DependencyB`\n"
);
}

View File

@@ -12,12 +12,15 @@ package org.springframework.ide.vscode.boot.java.livehover.test;
import static org.junit.Assert.assertTrue;
import java.nio.file.Path;
import java.time.Duration;
import java.util.Optional;
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.java.IJavaProject;
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;
@@ -213,10 +216,125 @@ public class BeanInjectedIntoHoverProviderTest {
"\n" +
"Bean [id: fooImplementation, type: `hello.FooImplementation`] injected into:\n" +
"\n" +
"- Bean [id: myController, type: `hello.MyController`]\n"
"- Bean: myController \n" +
" Type: `hello.MyController`\n"
);
}
@Test
public void beanWithFileResource() throws Exception {
Path of = 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: `hello/MyController.class`"
);
}
@Test
public void beanWithClasspathResource() throws Exception {
Path of = 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")
.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`"
);
}
private Path getOutputFolder() {
return harness.getProjectFinder().find(null).get().getClasspath().getOutputFolder();
}
@Test
public void beanWithMultipleInjections() throws Exception {
LiveBeansModel beans = LiveBeansModel.builder()
@@ -269,8 +387,10 @@ public class BeanInjectedIntoHoverProviderTest {
"\n" +
"Bean [id: fooImplementation, type: `hello.FooImplementation`] injected into:\n" +
"\n" +
"- Bean [id: myController, type: `hello.MyController`]\n" +
"- Bean [id: otherBean, type: `hello.OtherBean`]\n"
"- Bean: myController \n" +
" Type: `hello.MyController`\n" +
"- Bean: otherBean \n" +
" Type: `hello.OtherBean`\n"
);
}

View File

@@ -164,7 +164,8 @@ public class ComponentInjectionsHoverProviderTest {
"\n" +
"Bean [id: fooImplementation, type: `com.example.FooImplementation`] injected into:\n" +
"\n" +
"- Bean [id: myController, type: `com.example.MyController`]\n"
"- Bean: myController \n" +
" Type: `com.example.MyController`"
);
}
@@ -218,8 +219,10 @@ public class ComponentInjectionsHoverProviderTest {
"\n" +
"Bean [id: fooImplementation, type: `com.example.FooImplementation`] injected into:\n" +
"\n" +
"- Bean [id: myController, type: `com.example.MyController`]\n" +
"- Bean [id: otherBean, type: `com.example.OtherBean`]\n"
"- Bean: myController \n" +
" Type: `com.example.MyController`\n" +
"- Bean: otherBean \n" +
" Type: `com.example.OtherBean`"
);
}
@@ -275,15 +278,19 @@ public class ComponentInjectionsHoverProviderTest {
"\n" +
"Bean [id: fooImplementation, type: `com.example.FooImplementation`] injected into:\n" +
"\n" +
"- Bean [id: myController, type: `com.example.MyController`]\n" +
"- Bean [id: otherBean, type: `com.example.OtherBean`]\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 [id: myController, type: `com.example.MyController`]\n" +
"- Bean [id: otherBean, type: `com.example.OtherBean`]\n"
"- Bean: myController \n" +
" Type: `com.example.MyController`\n" +
"- Bean: otherBean \n" +
" Type: `com.example.OtherBean`\n"
);
}
@@ -342,7 +349,8 @@ public class ComponentInjectionsHoverProviderTest {
"\n" +
"Bean [id: fooImplementation, type: `com.example.FooImplementation`] injected into:\n" +
"\n" +
"- Bean [id: myController, type: `com.example.MyController`]"
"- Bean: myController \n" +
" Type: `com.example.MyController`"
);
}
@@ -401,7 +409,8 @@ public class ComponentInjectionsHoverProviderTest {
"\n" +
"Bean [id: alternateFooImplementation, type: `com.example.FooImplementation`] injected into:\n" +
"\n" +
"- Bean [id: otherBean, type: `com.example.OtherBean`]\n"
"- Bean: otherBean \n" +
" Type: `com.example.OtherBean`\n"
);
}