From 13a1ba088bf212bf4c6b6db7eb01ae07203571b6 Mon Sep 17 00:00:00 2001 From: BoykoAlex Date: Thu, 16 May 2019 14:24:10 -0400 Subject: [PATCH] PT #164318678: Live hovers for beans with non-standard ids --- .../AbstractInjectedIntoHoverProvider.java | 39 +++++-- .../BeanInjectedIntoHoverProvider.java | 2 +- .../ComponentInjectionsHoverProvider.java | 48 +++++--- .../test/BeansByTypeHoverProviderTest.java | 106 +++++++++++++++++- 4 files changed, 169 insertions(+), 26 deletions(-) diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/AbstractInjectedIntoHoverProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/AbstractInjectedIntoHoverProvider.java index 1ae5781e4..2e89fdfab 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/AbstractInjectedIntoHoverProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/AbstractInjectedIntoHoverProvider.java @@ -53,9 +53,15 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider public AbstractInjectedIntoHoverProvider(SourceLinks sourceLinks) { this.sourceLinks = sourceLinks; } + + @FunctionalInterface + protected interface DefinedBeanProvider { + LiveBean definedBean(SpringBootApp app); + } @Override - public Collection getLiveHintCodeLenses(IJavaProject project, Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) { + public Collection getLiveHintCodeLenses(IJavaProject project, Annotation annotation, TextDocument doc, + SpringBootApp[] runningApps) { // Highlight if any running app contains an instance of this component try { if (runningApps.length > 0) { @@ -64,8 +70,8 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider if (Stream.of(runningApps).anyMatch(app -> LiveHoverUtils.hasRelevantBeans(app, definedBean))) { Optional nameRange = ASTUtils.nameRange(doc, annotation); if (nameRange.isPresent()) { - List codeLenses = assembleCodeLenses(project, runningApps, definedBean, doc, nameRange.get(), annotation); - return codeLenses.isEmpty() ? ImmutableList.of(new CodeLens(nameRange.get())) : codeLenses; + List codeLenses = assembleCodeLenses(project, runningApps, app -> definedBean, doc, nameRange.get(), annotation); + return codeLenses; } } } @@ -83,7 +89,7 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider LiveBean definedBean = getDefinedBean(annotation); if (definedBean != null) { - Hover hover = assembleHover(project, runningApps, definedBean, annotation, true, true); + Hover hover = assembleHover(project, runningApps, app -> definedBean, annotation, true, true); if (hover != null) { Optional nameRange = ASTUtils.nameRange(doc, annotation); if (nameRange.isPresent()) { @@ -96,10 +102,18 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider return null; } - protected List assembleCodeLenses(IJavaProject project, SpringBootApp[] runningApps, LiveBean definedBean, + protected List assembleCodeLenses(IJavaProject project, SpringBootApp[] runningApps, DefinedBeanProvider definedBeanProvider, TextDocument doc, Range range, ASTNode node) { + boolean beanFound = false; for (SpringBootApp app : runningApps) { + + LiveBean definedBean = definedBeanProvider.definedBean(app); + if (definedBean == null) { + continue; + } + beanFound = true; + List relevantBeans = LiveHoverUtils.findRelevantBeans(app, definedBean); if (!relevantBeans.isEmpty()) { @@ -120,8 +134,9 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider List codeLenses = builder.build(); return codeLenses.isEmpty() ? ImmutableList.of(new CodeLens(range)) : codeLenses; } + } - return ImmutableList.of(); + return beanFound ? ImmutableList.of(new CodeLens(range)) : null; } protected List assembleCodeLenseForAutowired(List wiredBeans, IJavaProject project, SpringBootApp app, TextDocument doc, Range nameRange, ASTNode astNode) { @@ -134,13 +149,19 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider return Collections.emptyList(); } - protected Hover assembleHover(IJavaProject project, SpringBootApp[] runningApps, LiveBean definedBean, ASTNode astNode, boolean injected, boolean wired) { + protected Hover assembleHover(IJavaProject project, SpringBootApp[] runningApps, DefinedBeanProvider definedBeanProvider, ASTNode astNode, boolean injected, boolean wired) { StringBuilder hover = new StringBuilder(); for (SpringBootApp app : runningApps) { - List relevantBeans = LiveHoverUtils.findRelevantBeans(app, definedBean); + LiveBean definedBean = definedBeanProvider.definedBean(app); + + if (definedBean == null) { + continue; + } + List relevantBeans = LiveHoverUtils.findRelevantBeans(app, definedBean); + if (!relevantBeans.isEmpty()) { if (hover.length() > 0) { hover.append(" \n \n"); @@ -180,7 +201,7 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider } } - + protected List getRelevantInjectedIntoBeans(IJavaProject project, SpringBootApp app, LiveBean definedBean, List relevantBeans) { LiveBeansModel beans = app.getBeans(); if (relevantBeans != null) { diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/BeanInjectedIntoHoverProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/BeanInjectedIntoHoverProvider.java index ef011f61e..77e192ac4 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/BeanInjectedIntoHoverProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/BeanInjectedIntoHoverProvider.java @@ -127,7 +127,7 @@ public class BeanInjectedIntoHoverProvider extends AbstractInjectedIntoHoverProv if (beanAnnotation != null) { LiveBean definedBean = getDefinedBean(beanAnnotation); if (definedBean != null) { - Hover hover = assembleHover(project, runningApps, definedBean, parameter, false, true); + Hover hover = assembleHover(project, runningApps, app -> definedBean, parameter, false, true); if (hover != null) { hover.setRange(range); } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/ComponentInjectionsHoverProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/ComponentInjectionsHoverProvider.java index 31f6ac0d6..0b5522993 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/ComponentInjectionsHoverProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/ComponentInjectionsHoverProvider.java @@ -15,7 +15,6 @@ import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.Set; -import java.util.stream.Stream; import org.eclipse.jdt.core.Flags; import org.eclipse.jdt.core.dom.ASTNode; @@ -35,6 +34,7 @@ import org.springframework.ide.vscode.boot.java.links.SourceLinks; 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.BadLocationException; import org.springframework.ide.vscode.commons.util.StringUtil; @@ -102,18 +102,18 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP } @Override - public Collection getLiveHintCodeLenses(IJavaProject project, TypeDeclaration typeDeclaration, TextDocument doc, - SpringBootApp[] runningApps) { + public Collection getLiveHintCodeLenses(IJavaProject project, TypeDeclaration typeDeclaration, + TextDocument doc, SpringBootApp[] runningApps) { if (runningApps.length > 0 && !isComponentAnnotatedType(typeDeclaration)) { try { - LiveBean definedBean = getDefinedBeanForType(typeDeclaration, null); - if (definedBean != null) { - if (Stream.of(runningApps).anyMatch(app -> LiveHoverUtils.hasRelevantBeans(app, definedBean))) { - Optional nameRange = Optional.of(ASTUtils.nodeRegion(doc, typeDeclaration.getName()).asRange()); - if (nameRange.isPresent()) { - List codeLenses = assembleCodeLenses(project, runningApps, definedBean, doc, nameRange.get(), typeDeclaration); - return codeLenses.isEmpty() ? ImmutableList.of(new CodeLens(nameRange.get())) : codeLenses; - } + ITypeBinding beanType = typeDeclaration.resolveBinding(); + if (beanType != null) { + String id = getBeanId(null, beanType, Flags.isStatic(typeDeclaration.getModifiers())); + Optional nameRange = Optional.of(ASTUtils.nodeRegion(doc, typeDeclaration.getName()).asRange()); + if (nameRange.isPresent()) { + List codeLenses = assembleCodeLenses(project, runningApps, app -> definedBean(app, getBeanType(beanType), id), doc, + nameRange.get(), typeDeclaration); + return codeLenses.isEmpty() ? ImmutableList.of(new CodeLens(nameRange.get())) : codeLenses; } } } catch (Exception e) { @@ -122,16 +122,34 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP } return ImmutableList.of(); } + + private LiveBean definedBean(SpringBootApp app, String beanType, String possibleId) { + LiveBeansModel beans = app.getBeans(); + if (beans != null) { + if (beans.getBeansOfName(possibleId).isEmpty()) { + // try bean type if there is only one bean of such type + List liveBeanCandidates = beans.getBeansOfType(beanType); + if (liveBeanCandidates.size() == 1) { + return liveBeanCandidates.get(0); + } + } else { + // Just construct defined bean ourselves + return LiveBean.builder().id(possibleId).type(beanType).build(); + } + } + return null; + } @Override public Hover provideHover(ASTNode node, TypeDeclaration typeDeclaration, ITypeBinding type, int offset, TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) { if (runningApps.length > 0 && !isComponentAnnotatedType(typeDeclaration)) { - - LiveBean definedBean = getDefinedBeanForType(typeDeclaration, null); - if (definedBean != null) { - Hover hover = assembleHover(project, runningApps, definedBean, typeDeclaration, true, true); + ITypeBinding beanType = typeDeclaration.resolveBinding(); + if (beanType != null) { + String id = getBeanId(null, beanType, Flags.isStatic(typeDeclaration.getModifiers())); + + Hover hover = assembleHover(project, runningApps, app -> definedBean(app, getBeanType(beanType), id), typeDeclaration, true, true); if (hover != null) { SimpleName name = typeDeclaration.getName(); try { diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/BeansByTypeHoverProviderTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/BeansByTypeHoverProviderTest.java index d04ce7282..aa076830b 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/BeansByTypeHoverProviderTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/BeansByTypeHoverProviderTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2018 Pivotal, Inc. + * Copyright (c) 2018, 2019 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 @@ -141,6 +141,110 @@ public class BeansByTypeHoverProviderTest { "Process [PID=111, name=`the-app`]" ); } + + @Test + public void beanWithNonStandardId() throws Exception { + LiveBeansModel beans = LiveBeansModel.builder() + .add(LiveBean.builder() + .id("random") + .type("com.example.ScannedRandomClass") + .build() + ) + .add(LiveBean.builder() + .id("randomOtherBean") + .type("randomOtherBeanType") + .dependencies("random") + .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("ScannedRandomClass"); + editor.assertTrimmedHover("ScannedRandomClass", + "**→ `randomOtherBeanType`**\n" + + "- Bean: `randomOtherBean` \n" + + " Type: `randomOtherBeanType`\n" + + " \n" + + "Bean id: `random` \n" + + "Process [PID=111, name=`the-app`]" + ); + } + + @Test + public void beansWithNonStandardIdMoreThanOneOfSameType() throws Exception { + LiveBeansModel beans = LiveBeansModel.builder() + .add(LiveBean.builder() + .id("random") + .type("com.example.ScannedRandomClass") + .build() + ) + .add(LiveBean.builder() + .id("anotherRandom") + .type("com.example.ScannedRandomClass") + .build() + ) + .add(LiveBean.builder() + .id("randomOtherBean") + .type("randomOtherBeanType") + .dependencies("random") + .build() + ) + .add(LiveBean.builder() + .id("irrelevantBean") + .type("com.example.IrrelevantBean") + .dependencies("anotherRandom") + .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 {