PT #159866173: Add autowired beans for @Bean hover and codelens

This commit is contained in:
BoykoAlex
2018-08-21 16:58:50 -04:00
parent 01a15ec0ba
commit a4734b8029
5 changed files with 289 additions and 27 deletions

View File

@@ -172,7 +172,10 @@ public class AutowiredHoverProvider implements HoverProvider {
public static List<LiveBean> getRelevantAutowiredBeans(IJavaProject project, ASTNode declarationNode, SpringBootApp app, LiveBean definedBean) {
List<LiveBean> relevantBeans = LiveHoverUtils.findRelevantBeans(app, definedBean);
return getRelevantAutowiredBeans(project, declarationNode, app, relevantBeans);
}
public static List<LiveBean> getRelevantAutowiredBeans(IJavaProject project, ASTNode declarationNode, SpringBootApp app, List<LiveBean> relevantBeans) {
if (!relevantBeans.isEmpty()) {
List<LiveBean> allDependencyBeans = LiveHoverUtils.findAllDependencyBeans(app, relevantBeans);
@@ -199,12 +202,7 @@ public class AutowiredHoverProvider implements HoverProvider {
return ((List<Object>)methodDeclaration.parameters()).stream()
.filter(p -> p instanceof SingleVariableDeclaration)
.map(p -> (SingleVariableDeclaration)p)
.map(singleVariableDeclaration -> {
// Supposed to be a list of one bean for the variable declaration
List<LiveBean> matches = findAutowiredBeans(project, singleVariableDeclaration, beans);
return matches.isEmpty() ? null : matches.get(0);
})
.filter(matchedBean -> matchedBean != null)
.flatMap(singleVariableDeclaration -> findAutowiredBeans(project, singleVariableDeclaration, beans).stream())
.collect(Collectors.toList());
} else if (declarationNode instanceof FieldDeclaration) {
FieldDeclaration fieldDeclaration = (FieldDeclaration)declarationNode;

View File

@@ -64,7 +64,7 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider
if (Stream.of(runningApps).anyMatch(app -> LiveHoverUtils.hasRelevantBeans(app, definedBean))) {
Optional<Range> nameRange = ASTUtils.nameRange(doc, annotation);
if (nameRange.isPresent()) {
List<CodeLens> codeLenses = assembleCodeLenses(project, runningApps, definedBean, nameRange.get(), ASTUtils.getAnnotatedType(annotation) != null);
List<CodeLens> codeLenses = assembleCodeLenses(project, runningApps, definedBean, nameRange.get(), annotation);
return codeLenses.isEmpty() ? ImmutableList.of(new CodeLens(nameRange.get())) : codeLenses;
}
}
@@ -83,7 +83,7 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider
LiveBean definedBean = getDefinedBean(annotation);
if (definedBean != null) {
Hover hover = assembleHover(project, runningApps, definedBean, ASTUtils.getAnnotatedType(annotation) != null);
Hover hover = assembleHover(project, runningApps, definedBean, annotation);
if (hover != null) {
Optional<Range> nameRange = ASTUtils.nameRange(doc, annotation);
if (nameRange.isPresent()) {
@@ -96,7 +96,7 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider
return null;
}
protected List<CodeLens> assembleCodeLenses(IJavaProject project, SpringBootApp[] runningApps, LiveBean definedBean, Range range, boolean includeWiredBeans) {
protected List<CodeLens> assembleCodeLenses(IJavaProject project, SpringBootApp[] runningApps, LiveBean definedBean, Range range, ASTNode astNode) {
List<CodeLens> codeLensList = new ArrayList<>();
for (SpringBootApp app : runningApps) {
@@ -108,24 +108,26 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider
if (!injectedBeans.isEmpty()) {
// Break out of the loop. Just look for the first app with injected into beans
List<CodeLens> injectedCodeLenses = LiveHoverUtils.createCodeLensesForBeans(range, injectedBeans, BEANS_PREFIX, MAX_INLINE_BEANS_STRING_LENGTH, INLINE_BEANS_STRING_SEPARATOR);
builder.addAll(injectedCodeLenses == null ? ImmutableList.of(new CodeLens(range)) : injectedCodeLenses);
}
if (includeWiredBeans) {
List<LiveBean> allDependencyBeans = LiveHoverUtils.findAllDependencyBeans(app, relevantBeans);
List<CodeLens> wiredCodeLenses = LiveHoverUtils.createCodeLensesForBeans(range, allDependencyBeans,
AutowiredHoverProvider.BEANS_PREFIX, MAX_INLINE_BEANS_STRING_LENGTH,
INLINE_BEANS_STRING_SEPARATOR);
if (wiredCodeLenses != null) {
builder.addAll(wiredCodeLenses);
}
builder.addAll(injectedCodeLenses.isEmpty() ? ImmutableList.of(new CodeLens(range)) : injectedCodeLenses);
}
// Wired beans code lenses
List<LiveBean> wiredBeans = findWiredBeans(project, app, relevantBeans, astNode);
builder.addAll(LiveHoverUtils.createCodeLensesForBeans(range, wiredBeans,
AutowiredHoverProvider.BEANS_PREFIX, MAX_INLINE_BEANS_STRING_LENGTH,
INLINE_BEANS_STRING_SEPARATOR));
return builder.build();
}
}
return codeLensList;
}
protected Hover assembleHover(IJavaProject project, SpringBootApp[] runningApps, LiveBean definedBean, boolean includeWiredBeans) {
protected List<LiveBean> findWiredBeans(IJavaProject project, SpringBootApp app, List<LiveBean> relevantBeans, ASTNode astNode) {
return Collections.emptyList();
}
protected Hover assembleHover(IJavaProject project, SpringBootApp[] runningApps, LiveBean definedBean, ASTNode astNode) {
StringBuilder hover = new StringBuilder();
boolean hasContent = false;
@@ -170,11 +172,9 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider
hover.append("\n \n");
}
if (includeWiredBeans) {
List<LiveBean> allDependencyBeans = LiveHoverUtils.findAllDependencyBeans(app, relevantBeans);
if (!allDependencyBeans.isEmpty()) {
AutowiredHoverProvider.createHoverContentForBeans(server, definedBean, project, hover, allDependencyBeans);
}
List<LiveBean> wiredBeans = findWiredBeans(project, app, relevantBeans, astNode);
if (!wiredBeans.isEmpty()) {
AutowiredHoverProvider.createHoverContentForBeans(server, definedBean, project, hover, wiredBeans);
}
hover.append(LiveHoverUtils.niceAppName(app));

View File

@@ -10,13 +10,19 @@
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.livehover;
import java.util.Collections;
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.MethodDeclaration;
import org.springframework.ide.vscode.boot.java.BootJavaLanguageServerComponents;
import org.springframework.ide.vscode.boot.java.autowired.AutowiredHoverProvider;
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;
public class BeanInjectedIntoHoverProvider extends AbstractInjectedIntoHoverProvider {
@@ -66,4 +72,15 @@ public class BeanInjectedIntoHoverProvider extends AbstractInjectedIntoHoverProv
);
}
@Override
protected List<LiveBean> findWiredBeans(IJavaProject project, SpringBootApp app, List<LiveBean> relevantBeans, ASTNode astNode) {
if (astNode instanceof Annotation) {
MethodDeclaration beanMethod = ASTUtils.getAnnotatedMethod((Annotation) astNode);
if (beanMethod != null) {
return AutowiredHoverProvider.getRelevantAutowiredBeans(project, beanMethod, app, relevantBeans);
}
}
return Collections.emptyList();
}
}

View File

@@ -11,6 +11,7 @@
package org.springframework.ide.vscode.boot.java.livehover;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
@@ -110,7 +111,7 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP
if (Stream.of(runningApps).anyMatch(app -> LiveHoverUtils.hasRelevantBeans(app, definedBean))) {
Optional<Range> nameRange = Optional.of(ASTUtils.nodeRegion(doc, typeDeclaration.getName()).asRange());
if (nameRange.isPresent()) {
List<CodeLens> codeLenses = assembleCodeLenses(project, runningApps, definedBean, nameRange.get(), true);
List<CodeLens> codeLenses = assembleCodeLenses(project, runningApps, definedBean, nameRange.get(), typeDeclaration);
return codeLenses.isEmpty() ? ImmutableList.of(new CodeLens(nameRange.get())) : codeLenses;
}
}
@@ -130,7 +131,7 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP
LiveBean definedBean = getDefinedBeanForType(typeDeclaration, null);
if (definedBean != null) {
Hover hover = assembleHover(project, runningApps, definedBean, true);
Hover hover = assembleHover(project, runningApps, definedBean, typeDeclaration);
if (hover != null) {
SimpleName name = typeDeclaration.getName();
try {
@@ -145,6 +146,20 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP
return null;
}
@Override
protected List<LiveBean> findWiredBeans(IJavaProject project, SpringBootApp app, List<LiveBean> relevantBeans,
ASTNode astNode) {
TypeDeclaration typeDeclaration = null;
if (astNode instanceof TypeDeclaration) {
typeDeclaration = (TypeDeclaration) astNode;
} else if (astNode instanceof Annotation) {
typeDeclaration = ASTUtils.getAnnotatedType((Annotation) astNode);
}
return typeDeclaration == null ? Collections.emptyList() : LiveHoverUtils.findAllDependencyBeans(app, relevantBeans);
}
private boolean isComponentAnnotatedType(TypeDeclaration typeDeclaration) {
List<?> modifiers = typeDeclaration.modifiers();
for (Object modifier : modifiers) {

View File

@@ -39,6 +39,28 @@ public class BeanInjectedIntoHoverProviderTest {
" void doSomeFoo();\n" +
"}\n"
);
p.createType("hello.IDependency",
"package hello;\n" +
"\n" +
"public interface IDependency {\n" +
"}\n"
);
p.createType("hello.DependencyA",
"package hello;\n" +
"\n" +
"public class DependencyA implements IDependency {\n" +
"}\n"
);
p.createType("hello.DependencyB",
"package hello;\n" +
"\n" +
"public class DependencyB implements IDependency {\n" +
"}\n"
);
};
private BootJavaLanguageServerHarness harness;
@@ -484,4 +506,214 @@ public class BeanInjectedIntoHoverProviderTest {
editor.assertHighlights(/*NONE*/);
editor.assertNoHover("@Bean");
}
@Test
public void beanWithOneWiring() throws Exception {
LiveBeansModel beans = LiveBeansModel.builder()
.add(LiveBean.builder()
.id("fooImplementation")
.type("hello.FooImplementation")
.dependencies("depA")
.build()
)
.add(LiveBean.builder()
.id("depA")
.type("hello.DependencyA")
.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(DependencyA a) {\n" +
" return new FooImplementation();\n" +
" }\n" +
"}"
);
editor.assertHighlights("@Bean");
editor.assertTrimmedHover("@Bean",
"**Injected `fooImplementation` &rarr; _not injected anywhere_** \n" +
"**Autowired `fooImplementation` &larr; `depA`**\n" +
"- Bean: `depA` \n" +
" Type: `hello.DependencyA`\n" +
" \n" +
"Process [PID=111, name=`the-app`]"
);
}
@Test
public void beanWithMultipleWirings() throws Exception {
LiveBeansModel beans = LiveBeansModel.builder()
.add(LiveBean.builder()
.id("fooImplementation")
.type("hello.FooImplementation")
.dependencies("depA", "depB")
.build()
)
.add(LiveBean.builder()
.id("depA")
.type("hello.DependencyA")
.build()
)
.add(LiveBean.builder()
.id("depB")
.type("hello.DependencyB")
.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(DependencyA a, DependencyB b) {\n" +
" return new FooImplementation();\n" +
" }\n" +
"}"
);
editor.assertHighlights("@Bean");
editor.assertTrimmedHover("@Bean",
"**Injected `fooImplementation` &rarr; _not injected anywhere_** \n" +
"**Autowired `fooImplementation` &larr; `depA` `depB`**\n" +
"- Bean: `depA` \n" +
" Type: `hello.DependencyA`\n" +
"- Bean: `depB` \n" +
" Type: `hello.DependencyB`\n" +
" \n" +
"Process [PID=111, name=`the-app`]"
);
}
@Test
public void beanWithCollectionWiring() throws Exception {
LiveBeansModel beans = LiveBeansModel.builder()
.add(LiveBean.builder()
.id("fooImplementation")
.type("hello.FooImplementation")
.dependencies("depA", "depB")
.build()
)
.add(LiveBean.builder()
.id("depA")
.type("hello.DependencyA")
.build()
)
.add(LiveBean.builder()
.id("depB")
.type("hello.DependencyB")
.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(IDependency[] deps) {\n" +
" return new FooImplementation();\n" +
" }\n" +
"}"
);
editor.assertHighlights("@Bean");
editor.assertTrimmedHover("@Bean",
"**Injected `fooImplementation` &rarr; _not injected anywhere_** \n" +
"**Autowired `fooImplementation` &larr; `depA` `depB`**\n" +
"- Bean: `depA` \n" +
" Type: `hello.DependencyA`\n" +
"- Bean: `depB` \n" +
" Type: `hello.DependencyB`\n" +
" \n" +
"Process [PID=111, name=`the-app`]"
);
}
@Test
public void beanWithQualifierWiring() throws Exception {
LiveBeansModel beans = LiveBeansModel.builder()
.add(LiveBean.builder()
.id("fooImplementation")
.type("hello.FooImplementation")
.dependencies("depA", "depB")
.build()
)
.add(LiveBean.builder()
.id("depB")
.type("hello.DependencyB")
.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(@Qualifier(\"depB\") IDependency[] deps) {\n" +
" return new FooImplementation();\n" +
" }\n" +
"}"
);
editor.assertHighlights("@Bean");
editor.assertTrimmedHover("@Bean",
"**Injected `fooImplementation` &rarr; _not injected anywhere_** \n" +
"**Autowired `fooImplementation` &larr; `depB`**\n" +
"- Bean: `depB` \n" +
" Type: `hello.DependencyB`\n" +
" \n" +
"Process [PID=111, name=`the-app`]"
);
}
}