Merge branch 'master' of github.com:spring-projects/sts4

This commit is contained in:
Kris De Volder
2018-08-01 15:24:19 -07:00
35 changed files with 1735 additions and 518 deletions

View File

@@ -20,6 +20,7 @@ import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.FieldDeclaration;
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.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.TypeDeclaration;
@@ -40,6 +41,7 @@ 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.java.IType;
import org.springframework.ide.vscode.commons.util.BadLocationException;
import org.springframework.ide.vscode.commons.util.StringUtil;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
@@ -53,7 +55,7 @@ public class AutowiredHoverProvider implements HoverProvider {
final static Logger log = LoggerFactory.getLogger(AutowiredHoverProvider.class);
private static final int MAX_INLINE_BEANS_STRING_LENGTH = 50;
private static final int MAX_INLINE_BEANS_STRING_LENGTH = 60;
private static final String INLINE_BEANS_STRING_SEPARATOR = " ";
private BootJavaLanguageServerComponents server;
@@ -63,34 +65,31 @@ 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());
public Collection<Range> getLiveHoverHints(IJavaProject project, Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) {
if (runningApps.length > 0) {
LiveBean definedBean = getDefinedBeanForTypeDeclaration(ASTUtils.findDeclaringType(annotation));
// Annotation is MarkerNode, parent is some field, method, variable declaration node.
ASTNode declarationNode = annotation.getParent();
try {
Range hoverRange = doc.toRange(annotation.getStartPosition(), annotation.getLength());
return getLiveHoverHints(project, declarationNode, hoverRange, runningApps, definedBean);
} catch (BadLocationException e) {
log.error("", e);
}
}
return null;
}
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.error("", e);
}
private Collection<Range> getLiveHoverHints(IJavaProject project, ASTNode declarationNode, Range range,
SpringBootApp[] runningApps, LiveBean definedBean) {
if (declarationNode != null && definedBean != null) {
for (SpringBootApp app : runningApps) {
List<LiveBean> relevantBeans = getRelevantAutowiredBeans(project, declarationNode, app, definedBean);
if (!relevantBeans.isEmpty()) {
return ImmutableList.of(range);
}
}
}
catch (Exception e) {
log.error("", e);
}
return null;
}
@@ -98,74 +97,94 @@ public class AutowiredHoverProvider implements HoverProvider {
public Hover provideHover(ASTNode node, Annotation annotation, ITypeBinding type, int offset,
TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) {
if (runningApps.length > 0) {
LiveBean definedBean = getDefinedBeanForTypeDeclaration(ASTUtils.findDeclaringType(annotation));
// Annotation is MarkerNode, parent is some field, method, variable declaration node.
ASTNode declarationNode = annotation.getParent();
return provideHover(definedBean, declarationNode, offset, doc, project, runningApps);
}
return null;
}
private Hover provideHover(LiveBean definedBean, ASTNode declarationNode, int offset, TextDocument doc,
IJavaProject project, SpringBootApp[] runningApps) {
if (definedBean != null) {
StringBuilder hover = new StringBuilder();
LiveBean definedBean = getDefinedBean(annotation);
if (definedBean != null) {
boolean hasContent = false;
boolean hasContent = false;
for (SpringBootApp app : runningApps) {
for (SpringBootApp app : runningApps) {
LiveBeansModel beans = app.getBeans();
List<LiveBean> relevantBeans = LiveHoverUtils.findRelevantBeans(app, definedBean).collect(Collectors.toList());
List<LiveBean> autowiredBeans = getRelevantAutowiredBeans(project, declarationNode, app, definedBean);
if (!relevantBeans.isEmpty()) {
List<LiveBean> allDependencyBeans = relevantBeans.stream()
.flatMap(b -> Arrays.stream(b.getDependencies()))
.distinct()
.flatMap(d -> beans.getBeansOfName(d).stream())
.collect(Collectors.toList());
if (!allDependencyBeans.isEmpty()) {
// parent is marker node, grandparent is some field, method, variable declaration node.
ASTNode declarationNode = node.getParent().getParent();
List<LiveBean> autowiredBeans = findAutowiredBeans(project, declarationNode, allDependencyBeans);
if (autowiredBeans.isEmpty()) {
// Show all relevant dependency beans
autowiredBeans = allDependencyBeans;
}
if (!autowiredBeans.isEmpty()) {
if (!hasContent) {
hasContent = true;
} else {
hover.append(" \n \n");
}
hover.append("**Autowired &rarr; ");
if (LiveHoverUtils.doBeansFitInline(autowiredBeans, MAX_INLINE_BEANS_STRING_LENGTH, INLINE_BEANS_STRING_SEPARATOR)) {
hover.append(autowiredBeans.stream().map(b -> LiveHoverUtils.showBeanInline(server, project, b)).collect(Collectors.joining(INLINE_BEANS_STRING_SEPARATOR)));
hover.append("**\n");
} else {
hover.append(autowiredBeans.size());
hover.append(" beans**\n");
}
if (!autowiredBeans.isEmpty()) {
if (!hasContent) {
hasContent = true;
} else {
hover.append(" \n \n");
}
hover.append("**Autowired `");
hover.append(definedBean.getId());
hover.append("` &rarr; ");
if (LiveHoverUtils.doBeansFitInline(autowiredBeans, MAX_INLINE_BEANS_STRING_LENGTH - definedBean.getId().length(),
INLINE_BEANS_STRING_SEPARATOR)) {
hover.append(autowiredBeans.stream().map(b -> LiveHoverUtils.showBeanInline(server, project, b))
.collect(Collectors.joining(INLINE_BEANS_STRING_SEPARATOR)));
hover.append("**\n");
} else {
hover.append(autowiredBeans.size());
hover.append(" bean");
if (autowiredBeans.size() > 1) {
hover.append('s');
}
hover.append("**\n");
}
// if (autowiredBeans.size() == 1) {
// hover.append(LiveHoverUtils.showBeanIdAndTypeInline(server, project, autowiredBeans.get(0)));
// } else {
// hover.append(autowiredBeans.size());
// hover.append(" beans**\n");
// }
hover.append(autowiredBeans.stream()
.map(b -> "- " + LiveHoverUtils.showBeanWithResource(server, b, " ", project))
.collect(Collectors.joining("\n"))
);
hover.append("\n \n");
hover.append(LiveHoverUtils.niceAppName(app));
}
}
}
hover.append(autowiredBeans.stream()
.map(b -> "- " + LiveHoverUtils.showBeanWithResource(server, b, " ", project))
.collect(Collectors.joining("\n")));
hover.append("\n \n");
hover.append(LiveHoverUtils.niceAppName(app));
}
}
if (hasContent) {
return new Hover(ImmutableList.of(Either.forLeft(hover.toString())));
}
}
if (hasContent) {
return new Hover(ImmutableList.of(Either.forLeft(hover.toString())));
}
}
return null;
}
private List<LiveBean> getRelevantAutowiredBeans(IJavaProject project, ASTNode declarationNode, SpringBootApp app, LiveBean definedBean) {
LiveBeansModel beans = app.getBeans();
List<LiveBean> relevantBeans = LiveHoverUtils.findRelevantBeans(app, definedBean);
if (!relevantBeans.isEmpty()) {
List<LiveBean> allDependencyBeans = relevantBeans.stream()
.flatMap(b -> Arrays.stream(b.getDependencies())).distinct()
.flatMap(d -> beans.getBeansOfName(d).stream()).collect(Collectors.toList());
if (!allDependencyBeans.isEmpty()) {
List<LiveBean> autowiredBeans = findAutowiredBeans(project, declarationNode,
allDependencyBeans);
if (autowiredBeans.isEmpty()) {
// Show all relevant dependency beans
autowiredBeans = allDependencyBeans;
} else {
return autowiredBeans;
}
}
}
return Collections.emptyList();
}
@SuppressWarnings("unchecked")
private List<LiveBean> findAutowiredBeans(IJavaProject project, ASTNode declarationNode, Collection<LiveBean> beans) {
if (declarationNode instanceof MethodDeclaration) {
@@ -190,14 +209,17 @@ public class AutowiredHoverProvider implements HoverProvider {
if (type != null) {
String fqName = type.getQualifiedName();
if (fqName != null) {
relevant = matchBeans(project, beans, fqName);
relevant = matchBeans(project, beans, fqName, true);
if (relevant.isEmpty()) {
IType indexType = project.findType(fqName);
if (indexType != null) {
relevant = project.allSubtypesOf(indexType)
.map(subType -> matchBeans(project, beans, subType.getFullyQualifiedName()))
.map(subType -> matchBeans(project, beans, subType.getFullyQualifiedName(), false))
.filter(relevantBeans -> !relevantBeans.isEmpty())
.blockFirst();
if (relevant == null) {
relevant = Collections.emptyList();
}
}
}
}
@@ -205,48 +227,83 @@ public class AutowiredHoverProvider implements HoverProvider {
return relevant;
}
private List<LiveBean> matchBeans(IJavaProject project, Collection<LiveBean> beans, String fqName) {
private List<LiveBean> matchBeans(IJavaProject project, Collection<LiveBean> beans, String fqName, boolean allDots) {
if (fqName != null) {
return beans.stream().filter(b -> fqName.equals(b.getType(true))).collect(Collectors.toList());
if (allDots) {
return beans.stream().filter(b -> fqName.equals(b.getType(true).replace('$', '.'))).collect(Collectors.toList());
} else {
return beans.stream().filter(b -> fqName.equals(b.getType(true))).collect(Collectors.toList());
}
} else {
return Collections.emptyList();
}
}
private LiveBean getDefinedBean(Annotation autowiredAnnotation) {
TypeDeclaration declaringType = ASTUtils.findDeclaringType(autowiredAnnotation);
private LiveBean getDefinedBeanForTypeDeclaration(TypeDeclaration declaringType) {
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.
// 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) {
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();
.type(beanTypeName).build();
}
}
return null;
}
return null;
}
@Override
public Hover provideHover(ASTNode node, TypeDeclaration typeDeclaration, ITypeBinding type, int offset,
TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) {
return null;
public Hover provideHover(MethodDeclaration methodDeclaration, int offset, TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) {
LiveBean definedBean = getDefinedBeanForImplicitAutowiredConstructor(methodDeclaration);
return provideHover(definedBean, methodDeclaration, offset, doc, project, runningApps);
}
@Override
public Collection<Range> getLiveHoverHints(TypeDeclaration typeDeclaration, TextDocument doc, SpringBootApp[] runningApps) {
public Collection<Range> getLiveHoverHints(IJavaProject project, MethodDeclaration methodDeclaration, TextDocument doc,
SpringBootApp[] runningApps) {
LiveBean definedBean = getDefinedBeanForImplicitAutowiredConstructor(methodDeclaration);
try {
Range hoverRange = doc.toRange(methodDeclaration.getName().getStartPosition(), methodDeclaration.getName().getLength());
return getLiveHoverHints(project, methodDeclaration, hoverRange, runningApps, definedBean);
} catch (BadLocationException e) {
log.error("", e);
}
return null;
}
private LiveBean getDefinedBeanForImplicitAutowiredConstructor(MethodDeclaration methodDeclaration) {
if (methodDeclaration.isConstructor() && !methodDeclaration.parameters().isEmpty()) {
TypeDeclaration typeDeclaration = ASTUtils.findDeclaringType(methodDeclaration);
if (typeDeclaration != null && ASTUtils.hasExactlyOneConstructor(typeDeclaration) && !hasAutowiredAnnotation(methodDeclaration)) {
return getDefinedBeanForTypeDeclaration(typeDeclaration);
}
}
return null;
}
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) {
String fqName = typeBinding.getQualifiedName();
return Annotations.AUTOWIRED.equals(fqName) || Annotations.INJECT.equals(fqName);
}
}
}
return false;
}
}

View File

@@ -28,7 +28,6 @@ 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.LocalSpringBootApp;
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;
@@ -50,7 +49,7 @@ public class ConditionalsLiveHoverProvider implements HoverProvider {
}
@Override
public Collection<Range> getLiveHoverHints(Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) {
public Collection<Range> getLiveHoverHints(IJavaProject project, Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) {
try {
Optional<List<LiveConditional>> val = getMatchedLiveConditionals(annotation, runningApps);
if (val.isPresent()) {
@@ -156,16 +155,4 @@ public class ConditionalsLiveHoverProvider implements HoverProvider {
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

@@ -19,6 +19,7 @@ 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.MethodDeclaration;
import org.eclipse.jdt.core.dom.NodeFinder;
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.SimpleName;
@@ -140,6 +141,19 @@ public class BootJavaHoverProvider implements HoverHandler {
return super.visit(node);
}
@Override
public boolean visit(MethodDeclaration node) {
try {
extractLiveHintsForMethod(node, document, runningBootApps, result);
} catch (Exception e) {
Log.log(e);
}
return super.visit(node);
}
});
}
} catch (Exception e) {
@@ -149,13 +163,33 @@ public class BootJavaHoverProvider implements HoverHandler {
});
}
protected void extractLiveHintsForMethod(MethodDeclaration methodDeclaration, 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(project, methodDeclaration, 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 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);
Collection<Range> hints = provider.getLiveHoverHints(project, typeDeclaration, doc, runningApps);
if (hints!=null) {
result.addAll(hints);
}
@@ -175,7 +209,7 @@ public class BootJavaHoverProvider implements HoverHandler {
for (HoverProvider provider : this.hoverProviders.get(type)) {
getProject(doc).ifPresent(project -> {
if (hasActuatorDependency(project)) {
Collection<Range> hints = provider.getLiveHoverHints(annotation, doc, runningApps);
Collection<Range> hints = provider.getLiveHoverHints(project, annotation, doc, runningApps);
if (hints!=null) {
result.addAll(hints);
}
@@ -215,10 +249,29 @@ public class BootJavaHoverProvider implements HoverHandler {
}
// then do additional AST node coverage
if (node instanceof SimpleName && node.getParent() instanceof TypeDeclaration) {
return provideHoverForTypeDeclaration(node, (TypeDeclaration) node.getParent(), offset, doc, project);
if (node instanceof SimpleName) {
ASTNode parent = node.getParent();
if (parent instanceof TypeDeclaration) {
return provideHoverForTypeDeclaration(node, (TypeDeclaration) parent, offset, doc, project);
} else if (parent instanceof MethodDeclaration) {
return provideHoverForMethodDeclaration((MethodDeclaration) parent, offset, doc, project);
}
}
return null;
}
private Hover provideHoverForMethodDeclaration(MethodDeclaration methodDeclaration, int offset, TextDocument doc,
IJavaProject project) {
SpringBootApp[] runningApps = getRunningSpringApps(project);
if (runningApps.length > 0) {
for (HoverProvider provider : this.hoverProviders.getAll()) {
Hover hover = provider.provideHover(methodDeclaration, offset, doc, project, runningApps);
if (hover!=null) {
//TODO: compose multiple hovers somehow instead of just returning the first one?
return hover;
}
}
}
return null;
}

View File

@@ -15,6 +15,7 @@ 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.TypeDeclaration;
import org.eclipse.lsp4j.Hover;
import org.eclipse.lsp4j.Range;
@@ -27,10 +28,24 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument;
*/
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);
default Hover provideHover(ASTNode node, Annotation annotation, ITypeBinding type, int offset, TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) {
return null;
}
default Hover provideHover(ASTNode node, TypeDeclaration typeDeclaration, ITypeBinding type, int offset, TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) {
return null;
}
default Hover provideHover(MethodDeclaration methodDeclaration, int offset, TextDocument doc, IJavaProject project, SpringBootApp[] runningApps) {
return null;
}
Collection<Range> getLiveHoverHints(Annotation annotation, TextDocument doc, SpringBootApp[] runningApps);
Collection<Range> getLiveHoverHints(TypeDeclaration typeDeclaration, TextDocument doc, SpringBootApp[] runningApps);
default Collection<Range> getLiveHoverHints(IJavaProject project, Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) {
return null;
}
default Collection<Range> getLiveHoverHints(IJavaProject project,TypeDeclaration typeDeclaration, TextDocument doc, SpringBootApp[] runningApps) {
return null;
}
default Collection<Range> getLiveHoverHints(IJavaProject project, MethodDeclaration methodDeclaration, TextDocument doc, SpringBootApp[] runningApps) {
return null;
}
}

View File

@@ -33,19 +33,26 @@ public class RunningAppMatcher {
return RunningAppMatcher.doesProjectMatch(app, project);
}).collect(CollectorUtil.toImmutableList());
if (matchedProjects.size() > 0) {
return matchedProjects;
}
return matchedProjects;
}
return apps;
}
private static boolean doesProjectMatch(SpringBootApp app, IJavaProject project) {
if (doesProjectNameMatch(app, project)) return true;
if (doesProjectThinJarWrapperMatch(app, project)) return true;
if (doesClasspathMatch(app, project)) return true;
if (hasProjectName(app, project)) {
return doesProjectNameMatch(app, project);
}
return true;
}
return false;
public static boolean hasProjectName(SpringBootApp app, IJavaProject project) {
try {
String projectName = app.getSystemProperty("spring.boot.project.name");
return projectName != null && projectName.trim().length() > 0;
}
catch (Exception e) {
return false;
}
}
public static boolean doesProjectNameMatch(SpringBootApp app, IJavaProject project) {

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.stream.Collectors;
@@ -22,6 +23,8 @@ 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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.java.BootJavaLanguageServerComponents;
import org.springframework.ide.vscode.boot.java.handlers.HoverProvider;
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
@@ -29,14 +32,17 @@ 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.SimpleLanguageServer;
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 {
private static Logger LOG = LoggerFactory.getLogger(AbstractInjectedIntoHoverProvider.class);
private static final int MAX_INLINE_BEANS_STRING_LENGTH = 60;
private static final String INLINE_BEANS_STRING_SEPARATOR = " ";
protected BootJavaLanguageServerComponents server;
public AbstractInjectedIntoHoverProvider(BootJavaLanguageServerComponents server) {
@@ -44,7 +50,7 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider
}
@Override
public Collection<Range> getLiveHoverHints(Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) {
public Collection<Range> getLiveHoverHints(IJavaProject project, Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) {
// Highlight if any running app contains an instance of this component
try {
if (runningApps.length > 0) {
@@ -59,7 +65,7 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider
}
}
} catch (Exception e) {
Log.log(e);
LOG.error("", e);
}
return ImmutableList.of();
}
@@ -71,59 +77,80 @@ public abstract class AbstractInjectedIntoHoverProvider implements HoverProvider
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 assembleHover(project, runningApps, definedBean);
}
}
return null;
}
protected Hover assembleHover(IJavaProject project, SpringBootApp[] runningApps, LiveBean definedBean) {
StringBuilder hover = new StringBuilder();
boolean hasContent = false;
for (SpringBootApp app : runningApps) {
List<LiveBean> relevantBeans = LiveHoverUtils.findRelevantBeans(app, definedBean);
if (!relevantBeans.isEmpty()) {
List<LiveBean> injectedBeans = getRelevantInjectedIntoBeans(project, app, definedBean, relevantBeans);
if (!hasContent) {
hasContent = true;
} else {
hover.append(" \n \n");
}
if (injectedBeans.isEmpty()) {
hover.append("**Injected `");
hover.append(definedBean.getId());
hover.append("` &rarr; _not injected anywhere_** \n");
} else {
hover.append("**Injected `");
hover.append(definedBean.getId());
hover.append("` &rarr; ");
if (LiveHoverUtils.doBeansFitInline(injectedBeans, MAX_INLINE_BEANS_STRING_LENGTH - definedBean.getId().length(),
INLINE_BEANS_STRING_SEPARATOR)) {
hover.append(injectedBeans.stream().map(b -> LiveHoverUtils.showBeanInline(server, project, b))
.collect(Collectors.joining(INLINE_BEANS_STRING_SEPARATOR)));
hover.append("**\n");
} else {
hover.append(injectedBeans.size());
hover.append(" bean");
if (injectedBeans.size() > 1) {
hover.append('s');
}
hover.append("**\n");
}
hover.append(injectedBeans.stream()
.map(b -> "- " + LiveHoverUtils.showBeanWithResource(server, b, " ", project))
.collect(Collectors.joining("\n")));
hover.append("\n \n");
}
hover.append(LiveHoverUtils.niceAppName(app));
}
}
if (hasContent) {
return new Hover(ImmutableList.of(Either.forLeft(hover.toString())));
} else {
return null;
}
}
protected List<LiveBean> getRelevantInjectedIntoBeans(IJavaProject project, SpringBootApp app, LiveBean definedBean, List<LiveBean> relevantBeans) {
LiveBeansModel beans = app.getBeans();
if (relevantBeans != null) {
return relevantBeans.stream()
.flatMap(b -> beans.getBeansDependingOn(b.getId()).stream())
.distinct()
.collect(Collectors.toList());
}
return Collections.emptyList();
}
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(server, dependingBean, " ", project));
firstDependency = false;
}
}
}
}

View File

@@ -22,7 +22,6 @@ 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;
@@ -81,7 +80,7 @@ public class ActiveProfilesProvider implements HoverProvider {
}
@Override
public Collection<Range> getLiveHoverHints(Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) {
public Collection<Range> getLiveHoverHints(IJavaProject project, Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) {
if (runningApps.length > 0) {
Builder<Range> ranges = ImmutableList.builder();
nameRange(doc, annotation).ifPresent(ranges::add);
@@ -130,16 +129,4 @@ public class ActiveProfilesProvider implements HoverProvider {
}
}
@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

@@ -10,24 +10,14 @@
*******************************************************************************/
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.BootJavaLanguageServerComponents;
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.languageserver.util.SimpleLanguageServer;
import org.springframework.ide.vscode.commons.util.Optionals;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
public class BeanInjectedIntoHoverProvider extends AbstractInjectedIntoHoverProvider {
@@ -76,16 +66,4 @@ public class BeanInjectedIntoHoverProvider extends AbstractInjectedIntoHoverProv
);
}
@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

@@ -14,27 +14,23 @@ import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.Set;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.java.Annotations;
import org.springframework.ide.vscode.boot.java.BootJavaLanguageServerComponents;
import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchies;
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;
@@ -42,53 +38,12 @@ import com.google.common.collect.ImmutableList;
public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverProvider {
private static Logger LOG = LoggerFactory.getLogger(ComponentInjectionsHoverProvider.class);
public ComponentInjectionsHoverProvider(BootJavaLanguageServerComponents server) {
super(server);
}
@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(server, 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) {
String fqName = typeBinding.getQualifiedName();
return Annotations.AUTOWIRED.equals(fqName) || Annotations.INJECT.equals(fqName);
}
}
}
return false;
}
@Override
protected LiveBean getDefinedBean(Annotation annotation) {
return getDefinedBeanForComponent(annotation);
@@ -106,7 +61,7 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP
if (beanType != null) {
String id = getBeanId(annotation, beanType);
if (StringUtil.hasText(id)) {
return LiveBean.builder().id(id).type(beanType.getQualifiedName()).build();
return LiveBean.builder().id(id).type(getBeanType(beanType).toString()).build();
}
}
}
@@ -119,9 +74,8 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP
String typeName = beanType.getName();
ITypeBinding declaringClass = beanType.getDeclaringClass();
while (declaringClass != null) {
typeName = declaringClass.getName() + "." + typeName;
declaringClass = declaringClass.getDeclaringClass();
if (declaringClass != null) {
return getBeanType(beanType).toString();
}
if (StringUtil.hasText(typeName)) {
@@ -131,8 +85,20 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP
});
}
private static StringBuilder getBeanType(ITypeBinding beanType) {
ITypeBinding declaringClass = beanType.getDeclaringClass();
if (declaringClass == null) {
return new StringBuilder(beanType.getQualifiedName());
} else {
StringBuilder sb = getBeanType(declaringClass);
sb.append('$');
sb.append(beanType.getName());
return sb;
}
}
@Override
public Collection<Range> getLiveHoverHints(TypeDeclaration typeDeclaration, TextDocument doc,
public Collection<Range> getLiveHoverHints(IJavaProject project, TypeDeclaration typeDeclaration, TextDocument doc,
SpringBootApp[] runningApps) {
if (runningApps.length > 0 && !isComponentAnnotatedType(typeDeclaration)) {
try {
@@ -146,7 +112,7 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP
}
}
} catch (Exception e) {
Log.log(e);
LOG.error("", e);
}
}
return ImmutableList.of();
@@ -160,30 +126,7 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP
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 assembleHover(project, runningApps, definedBean);
}
}
return null;

View File

@@ -11,8 +11,10 @@
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.stream.Stream;
import java.util.stream.Collectors;
import org.springframework.ide.vscode.boot.java.BootJavaLanguageServerComponents;
import org.springframework.ide.vscode.boot.java.links.SourceLinkFactory;
@@ -131,20 +133,25 @@ public class LiveHoverUtils {
}
public static boolean hasRelevantBeans(SpringBootApp app, LiveBean definedBean) {
return findRelevantBeans(app, definedBean).findAny().isPresent();
return findRelevantBeans(app, definedBean).stream().findAny().isPresent();
}
public static Stream<LiveBean> findRelevantBeans(SpringBootApp app, LiveBean definedBean) {
public static List<LiveBean> findRelevantBeans(SpringBootApp app, LiveBean definedBean) {
LiveBeansModel beansModel = app.getBeans();
if (beansModel != null) {
Stream<LiveBean> relevantBeans = beansModel.getBeansOfName(definedBean.getId()).stream();
List<LiveBean> relevantBeans = beansModel.getBeansOfName(definedBean.getId());
String type = definedBean.getType();
if (type != null) {
relevantBeans = relevantBeans.filter(bean -> type.equals(bean.getType(true)));
// TODO: check if we should check for bean type rather than id that we build ourselves based on type
// if (relevantBeans.isEmpty()) {
// relevantBeans = beansModel.getBeansOfType(type);
// } else {
relevantBeans = relevantBeans.stream().filter(bean -> type.equals(bean.getType(true))).collect(Collectors.toList());
// }
}
return relevantBeans;
}
return Stream.empty();
return Collections.emptyList();
}
public static String niceAppName(SpringBootApp app) {

View File

@@ -22,7 +22,6 @@ 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;
@@ -55,7 +54,7 @@ public class RequestMappingHoverProvider implements HoverProvider {
}
@Override
public Collection<Range> getLiveHoverHints(Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) {
public Collection<Range> getLiveHoverHints(IJavaProject project, Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) {
try {
if (runningApps.length > 0) {
List<Tuple2<RequestMapping, SpringBootApp>> val = getRequestMappingMethodFromRunningApp(annotation, runningApps);
@@ -173,16 +172,4 @@ public class RequestMappingHoverProvider implements HoverProvider {
}
}
@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

@@ -10,7 +10,6 @@
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.utils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
@@ -128,8 +127,7 @@ public class ASTUtils {
return Optional.empty();
}
public static TypeDeclaration findDeclaringType(Annotation annotation) {
ASTNode node = annotation;
public static TypeDeclaration findDeclaringType(ASTNode node) {
while (node != null && !(node instanceof TypeDeclaration)) {
node = node.getParent();
}
@@ -137,20 +135,21 @@ public class ASTUtils {
return node != null ? (TypeDeclaration) node : null;
}
public static MethodDeclaration[] findConstructors(TypeDeclaration typeDecl) {
List<MethodDeclaration> constructors = new ArrayList<>();
public static boolean hasExactlyOneConstructor(TypeDeclaration typeDecl) {
boolean oneFound = false;
MethodDeclaration[] methods = typeDecl.getMethods();
for (MethodDeclaration methodDeclaration : methods) {
if (methodDeclaration.isConstructor()) {
constructors.add(methodDeclaration);
if (oneFound) {
return false;
} else {
oneFound = true;
}
}
}
return constructors.toArray(new MethodDeclaration[constructors.size()]);
return oneFound;
}
public static MethodDeclaration getAnnotatedMethod(Annotation annotation) {
ASTNode parent = annotation.getParent();
if (parent instanceof MethodDeclaration) {

View File

@@ -10,7 +10,6 @@
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.value;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
@@ -67,11 +66,6 @@ public class ValueHoverProvider implements HoverProvider {
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 {
@@ -206,10 +200,4 @@ public class ValueHoverProvider implements HoverProvider {
return null;
}
@Override
public Collection<Range> getLiveHoverHints(TypeDeclaration typeDeclaration, TextDocument doc,
SpringBootApp[] runningApps) {
return null;
}
}

View File

@@ -14,6 +14,8 @@ import static org.junit.Assert.assertTrue;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.junit.Before;
import org.junit.Test;
@@ -84,6 +86,34 @@ public class AutowiredHoverProviderTest {
"}\n"
);
p.createType("com.example.RuntimeBeanFactory",
Stream.of(
"package com.example;",
"public interface RuntimeBeanFactory {",
"void createRuntimeBean(String info);",
"}"
).collect(Collectors.joining("\n"))
);
p.createType("com.example.SomeComponent",
Stream.of("package com.example;",
"",
"import org.springframework.context.annotation.Bean;",
"",
// "@Component",
"public class SomeComponent {",
"",
"@Bean",
"public RuntimeBeanFactory getBeanFactory() {",
"\treturn new RuntimeBeanFactory() {",
"\t\tpublic void createRuntimeBean(String info){}",
"\t};",
"}",
"",
"}"
).collect(Collectors.joining("\n"))
);
p.createType("com.example.FooImplementation", FOO_IMPL_CONTENTS);
};
@@ -146,7 +176,7 @@ public class AutowiredHoverProviderTest {
editor.assertHighlights("@Component", "@Inject");
editor.assertTrimmedHover("@Inject",
"**Autowired &rarr; `dependencyA`**\n" +
"**Autowired `autowiredClass` &rarr; `dependencyA`**\n" +
"- Bean: `dependencyA` \n" +
" Type: `com.example.DependencyA` \n" +
" Resource: `" + Paths.get("com/example/DependencyA.class") + "`\n" +
@@ -201,7 +231,7 @@ public class AutowiredHoverProviderTest {
editor.assertHighlights("@Component", "@Autowired");
editor.assertTrimmedHover("@Autowired",
"**Autowired &rarr; `dependencyA` `dependencyB`**\n" +
"**Autowired `autowiredClass` &rarr; `dependencyA` `dependencyB`**\n" +
"- Bean: `dependencyA` \n" +
" Type: `com.example.DependencyA` \n" +
" Resource: `" + Paths.get("com/example/DependencyA.class") + "`\n" +
@@ -352,11 +382,11 @@ public class AutowiredHoverProviderTest {
Editor editor = harness.newEditor(LanguageId.JAVA, FOO_IMPL_CONTENTS);
editor.assertHighlights("@Component", "@Autowired", "@Autowired");
editor.assertHoverContains("@Autowired", 1,
"**Autowired &rarr; `superBean`**\n" +
"**Autowired `defaultFoo` &rarr; `superBean`**\n" +
"- Bean: `superBean` \n" +
" Type: `com.example.FooImplementation`");
editor.assertHoverContains("@Autowired", 2,
"**Autowired &rarr; `scheduler`**\n" +
"**Autowired `defaultFoo` &rarr; `scheduler`**\n" +
"- Bean: `scheduler` \n" +
" Type: `org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler`");
}
@@ -406,13 +436,220 @@ public class AutowiredHoverProviderTest {
);
editor.assertHighlights("@Controller", "@Autowired");
editor.assertHoverContains("@Autowired",
"**Autowired &rarr; `restTemplate`**\n" +
"**Autowired `myController` &rarr; `restTemplate`**\n" +
"- Bean: `restTemplate` \n" +
" Type: `org.springframework.web.client.RestTemplate`"
);
editor.assertHoverContains("@Controller",
"**Injection report for Bean [id: myController, type: `com.example.MyController`]**"
"**Injected `myController` &rarr; _not injected anywhere_** \n" +
"Process [PID=111, name=`the-app`]"
);
}
@Test
public void implicitAutowiringSingleConstructor() throws Exception {
LiveBeansModel beans = LiveBeansModel.builder()
.add(LiveBean.builder()
.id("someComponent")
.type("com.example.SomeComponent")
.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.stereotype.Component;\n" +
"\n" +
"@Component\n" +
"public class SomeComponent {\n" +
"\n" +
" private DepedencyA depA;\n" +
" private DepedencyB depB;\n" +
"\n" +
" public SomeComponent(DependencyA depA, DependencyB depB) {\n" +
" this.depA = depA;\n" +
" this.depB = depB;\n" +
" }\n" +
"}\n"
);
editor.assertHighlights("@Component", "SomeComponent");
editor.assertTrimmedHover("SomeComponent", 2,
"**Autowired `someComponent` &rarr; `dependencyA` `dependencyB`**\n" +
"- Bean: `dependencyA` \n" +
" Type: `com.example.DependencyA`\n" +
"- Bean: `dependencyB` \n" +
" Type: `com.example.DependencyB`\n" +
" \n" +
"Process [PID=111, name=`the-app`]\n"
);
}
@Test
public void noImplicitAutowiringForConstructorFromNonBean() throws Exception {
LiveBeansModel beans = LiveBeansModel.builder()
.add(LiveBean.builder()
.id("someOtherComponent")
.type("com.example.SomeOtherComponent")
.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" +
"public class SomeComponent {\n" +
"\n" +
" private DepedencyA depA;\n" +
" private DepedencyB depB;\n" +
"\n" +
" public SomeComponent(DependencyA depA, DependencyB depB) {\n" +
" this.depA = depA;\n" +
" this.depB = depB;\n" +
" }\n" +
"}\n"
);
editor.assertHighlights();
for (int i = 1; i < 2; i++) {
editor.assertNoHover("SomeComponent", i);
}
}
@Test
public void noImplicitAutowiringForMultipleConstructors() throws Exception {
LiveBeansModel beans = LiveBeansModel.builder()
.add(LiveBean.builder()
.id("someComponent")
.type("com.example.SomeComponent")
.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.stereotype.Component;\n" +
"\n" +
"@Component\n" +
"public class SomeComponent {\n" +
"\n" +
" private DepedencyA depA;\n" +
" private DepedencyB depB;\n" +
"\n" +
" public SomeComponent() {\n" +
" }\n" +
"\n" +
" public SomeComponent(DependencyA depA, DependencyB depB) {\n" +
" this.depA = depA;\n" +
" this.depB = depB;\n" +
" }\n" +
"}\n"
);
editor.assertHighlights("@Component");
for (int i = 1; i < 3; i++) {
editor.assertNoHover("SomeComponent", i);
}
}
@Test
public void anonymousInnerClassBeanWiring() throws Exception {
LiveBeansModel beans = LiveBeansModel.builder()
.add(LiveBean.builder()
.id("anotherComponent")
.type("com.example.AnotherComponent")
.dependencies("anonymousBeanFactory")
.build()
)
.add(LiveBean.builder()
.id("anonymousBeanFactory")
.type("com.example.SomeComponent$1")
.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 AnotherComponent {\n" +
"\n" +
" @Autowired\n" +
" RuntimeBeanFactory beanFactory;\n" +
"}\n"
);
editor.assertHighlights("@Component", "@Autowired");
editor.assertTrimmedHover("@Autowired", 1,
"**Autowired `anotherComponent` &rarr; `anonymousBeanFactory`**\n" +
"- Bean: `anonymousBeanFactory` \n" +
" Type: `com.example.SomeComponent$1`\n" +
" \n" +
"Process [PID=111, name=`the-app`]\n"
);
}
}

View File

@@ -95,11 +95,8 @@ public class BeanInjectedIntoHoverProviderTest {
);
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"
"**Injected `myFoo` &rarr; _not injected anywhere_** \n" +
"Process [PID=111, name=`the-app`]"
);
}
@@ -154,11 +151,8 @@ public class BeanInjectedIntoHoverProviderTest {
);
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"
"**Injected `beanId` &rarr; _not injected anywhere_** \n" +
"Process [PID=111, name=`the-app`]"
);
}
}
@@ -209,14 +203,11 @@ public class BeanInjectedIntoHoverProviderTest {
);
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" +
"**Injected `fooImplementation` &rarr; `myController`**\n" +
"- Bean: `myController` \n" +
" Type: `hello.MyController`\n"
" Type: `hello.MyController`\n" +
" \n" +
"Process [PID=111, name=`the-app`]"
);
}
@@ -269,14 +260,11 @@ public class BeanInjectedIntoHoverProviderTest {
);
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" +
"**Injected `fooImplementation` &rarr; `myController`**\n" +
"- Bean: `myController` \n" +
" Type: `hello.MyController`\n"
" Type: `hello.MyController`\n" +
" \n" +
"Process [PID=111, name=`the-app`]"
);
}
@@ -323,15 +311,12 @@ public class BeanInjectedIntoHoverProviderTest {
);
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" +
"**Injected `fooImplementation` &rarr; `myController`**\n" +
"- Bean: `myController` \n" +
" Type: `hello.MyController` \n" +
" Resource: `" + Paths.get("hello/MyController.class") + "`"
" Resource: `" + Paths.get("hello/MyController.class") + "`\n" +
" \n" +
"Process [PID=111, name=`the-app`]"
);
}
@@ -377,15 +362,12 @@ public class BeanInjectedIntoHoverProviderTest {
);
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" +
"**Injected `fooImplementation` &rarr; `myController`**\n" +
"- Bean: `myController` \n" +
" Type: `hello.MyController` \n" +
" Resource: `hello/MyController.class`"
" Resource: `hello/MyController.class`\n" +
" \n" +
"Process [PID=111, name=`the-app`]"
);
}
@@ -435,16 +417,13 @@ public class BeanInjectedIntoHoverProviderTest {
);
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" +
"**Injected `fooImplementation` &rarr; `myController` `otherBean`**\n" +
"- Bean: `myController` \n" +
" Type: `hello.MyController`\n" +
"- Bean: `otherBean` \n" +
" Type: `hello.OtherBean`\n"
" Type: `hello.OtherBean`\n" +
" \n" +
"Process [PID=111, name=`the-app`]"
);
}

View File

@@ -134,14 +134,11 @@ public class BeansByTypeHoverProviderTest {
);
editor.assertHighlights("ScannedRandomClass");
editor.assertTrimmedHover("ScannedRandomClass",
"**Injection report for Bean [id: scannedRandomClass, type: `com.example.ScannedRandomClass`]**\n" +
"\n" +
"Process [PID=111, name=`the-app`]:\n" +
"\n" +
"Bean [id: scannedRandomClass, type: `com.example.ScannedRandomClass`] injected into:\n" +
"\n" +
"**Injected `scannedRandomClass` &rarr; `randomOtherBean`**\n" +
"- Bean: `randomOtherBean` \n" +
" Type: `randomOtherBeanType`"
" Type: `randomOtherBeanType`\n" +
" \n" +
"Process [PID=111, name=`the-app`]"
);
}
@@ -190,14 +187,12 @@ public class BeansByTypeHoverProviderTest {
);
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" +
"**Injected `scannedFunctionClass` &rarr; 1 bean**\n" +
"- Bean: `org.springframework.cloud.function.context.config.ContextFunctionCatalogAutoConfiguration` \n" +
" Type: `org.springframework.cloud.function.context.config.ContextFunctionCatalogAutoConfiguration`"
" Type: `org.springframework.cloud.function.context.config.ContextFunctionCatalogAutoConfiguration`\n" +
" \n" +
"Process [PID=111, name=`the-app`]"
);
}
@@ -233,11 +228,8 @@ public class BeansByTypeHoverProviderTest {
);
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"
"**Injected `fooImplementation` &rarr; _not injected anywhere_** \n" +
"Process [PID=111, name=`the-app`]"
);
}

View File

@@ -12,7 +12,6 @@ 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;
@@ -107,11 +106,8 @@ public class ComponentInjectionsHoverProviderTest {
);
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"
"**Injected `fooImplementation` &rarr; _not injected anywhere_** \n" +
"Process [PID=111, name=`the-app`]"
);
}
@@ -159,14 +155,11 @@ public class ComponentInjectionsHoverProviderTest {
);
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" +
"**Injected `fooImplementation` &rarr; `myController`**\n" +
"- Bean: `myController` \n" +
" Type: `com.example.MyController`"
" Type: `com.example.MyController`\n" +
" \n" +
"Process [PID=111, name=`the-app`]\n"
);
}
@@ -214,16 +207,13 @@ public class ComponentInjectionsHoverProviderTest {
);
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" +
"**Injected `fooImplementation` &rarr; `myController` `otherBean`**\n" +
"- Bean: `myController` \n" +
" Type: `com.example.MyController`\n" +
"- Bean: `otherBean` \n" +
" Type: `com.example.OtherBean`"
" Type: `com.example.OtherBean`\n" +
" \n" +
"Process [PID=111, name=`the-app`]"
);
}
@@ -273,25 +263,21 @@ public class ComponentInjectionsHoverProviderTest {
);
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" +
"**Injected `fooImplementation` &rarr; `myController` `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" +
" \n" +
"Process [PID=1001, name=`app-instance-1`]" +
" \n \n" +
"**Injected `fooImplementation` &rarr; `myController` `otherBean`**\n" +
"- Bean: `myController` \n" +
" Type: `com.example.MyController`\n" +
"- Bean: `otherBean` \n" +
" Type: `com.example.OtherBean`\n"
" Type: `com.example.OtherBean`\n" +
" \n" +
"Process [PID=1002, name=`app-instance-2`]"
);
}
@@ -344,14 +330,11 @@ public class ComponentInjectionsHoverProviderTest {
);
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" +
"**Injected `fooImplementation` &rarr; `myController`**\n" +
"- Bean: `myController` \n" +
" Type: `com.example.MyController`"
" Type: `com.example.MyController`\n" +
" \n" +
"Process [PID=111, name=`the-app`]"
);
}
@@ -404,14 +387,11 @@ public class ComponentInjectionsHoverProviderTest {
);
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" +
"**Injected `alternateFooImplementation` &rarr; `otherBean`**\n" +
"- Bean: `otherBean` \n" +
" Type: `com.example.OtherBean`\n"
" Type: `com.example.OtherBean`\n" +
" \n" +
"Process [PID=111, name=`the-app`]"
);
}
@@ -451,7 +431,7 @@ public class ComponentInjectionsHoverProviderTest {
" }\n" +
"}\n"
);
editor.assertHighlights(/*MONE*/);
editor.assertHighlights(/*NONE*/);
editor.assertNoHover("@Component");
}
@@ -516,22 +496,10 @@ public class ComponentInjectionsHoverProviderTest {
" }\n" +
"}\n"
);
editor.assertHighlights("@Component");
editor.assertHighlights("@Component", "AutowiredClass");
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`"
"**Injected `autowiredClass` &rarr; _not injected anywhere_** \n" +
"Process [PID=111, name=`the-app`]\n"
);
}
@@ -578,11 +546,8 @@ public class ComponentInjectionsHoverProviderTest {
);
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"
"**Injected `autowiredClass` &rarr; _not injected anywhere_** \n" +
"Process [PID=111, name=`the-app`]\n"
);
}
@@ -621,7 +586,8 @@ public class ComponentInjectionsHoverProviderTest {
);
editor.assertHighlights("@SpringBootApplication");
editor.assertHoverContains("@SpringBootApplication",
"**Injection report for Bean [id: demoApplication, type: `com.example.DemoApplication`]**"
"**Injected `demoApplication` &rarr; _not injected anywhere_** \n" +
"Process [PID=111, name=`the-app`]"
);
}
@@ -629,7 +595,7 @@ public class ComponentInjectionsHoverProviderTest {
public void componentFromInnerClass() throws Exception {
LiveBeansModel beans = LiveBeansModel.builder()
.add(LiveBean.builder()
.id("demoApplication.InnerClass")
.id("com.example.DemoApplication$InnerClass")
.type("com.example.DemoApplication$InnerClass")
.build()
)
@@ -662,7 +628,8 @@ public class ComponentInjectionsHoverProviderTest {
);
editor.assertHighlights("@SpringBootApplication");
editor.assertHoverContains("@SpringBootApplication",
"**Injection report for Bean [id: demoApplication.InnerClass, type: `com.example.DemoApplication.InnerClass`]**"
"**Injected `com.example.DemoApplication$InnerClass` &rarr; _not injected anywhere_** \n" +
"Process [PID=111, name=`the-app`]"
);
}
@@ -670,7 +637,7 @@ public class ComponentInjectionsHoverProviderTest {
public void componentFromInnerInnerClass() throws Exception {
LiveBeansModel beans = LiveBeansModel.builder()
.add(LiveBean.builder()
.id("demoApplication.InnerClass.InnerInnerClass")
.id("com.example.DemoApplication$InnerClass$InnerInnerClass")
.type("com.example.DemoApplication$InnerClass$InnerInnerClass")
.build()
)
@@ -706,7 +673,8 @@ public class ComponentInjectionsHoverProviderTest {
);
editor.assertHighlights("@SpringBootApplication");
editor.assertHoverContains("@SpringBootApplication",
"**Injection report for Bean [id: demoApplication.InnerClass.InnerInnerClass, type: `com.example.DemoApplication.InnerClass.InnerInnerClass`]**"
"**Injected `com.example.DemoApplication$InnerClass$InnerInnerClass` &rarr; _not injected anywhere_** \n" +
"Process [PID=111, name=`the-app`]"
);
}
}