replaced live hover implementation for autowired annotation with new one
This commit is contained in:
@@ -10,20 +10,21 @@
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.autowired;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.TypeDeclaration;
|
||||
import org.eclipse.lsp4j.Hover;
|
||||
import org.eclipse.lsp4j.MarkedString;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.eclipse.lsp4j.jsonrpc.messages.Either;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.HoverProvider;
|
||||
import org.springframework.ide.vscode.boot.java.livehover.ASTUtils;
|
||||
import org.springframework.ide.vscode.boot.java.livehover.LiveHoverUtils;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBeansModel;
|
||||
@@ -38,50 +39,6 @@ import com.google.common.collect.ImmutableList;
|
||||
*/
|
||||
public class AutowiredHoverProvider implements HoverProvider {
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Hover> provideHover(ASTNode node, Annotation annotation,
|
||||
ITypeBinding type, int offset, TextDocument doc, SpringBootApp[] runningApps) {
|
||||
|
||||
SpringBootAppProvider[] bootApps = new SpringBootAppProvider[runningApps.length];
|
||||
for (int i = 0; i < runningApps.length; i++) {
|
||||
bootApps[i] = new SpringBootAppProviderImpl(runningApps[i]);
|
||||
}
|
||||
return provideHover(node, annotation, type, offset, doc, bootApps);
|
||||
}
|
||||
|
||||
public CompletableFuture<Hover> provideHover(ASTNode node, Annotation annotation,
|
||||
ITypeBinding type, int offset, TextDocument doc, SpringBootAppProvider[] runningApps) {
|
||||
try {
|
||||
List<Either<String, MarkedString>> hoverContent = new ArrayList<>();
|
||||
|
||||
for (SpringBootAppProvider bootApp : runningApps) {
|
||||
try {
|
||||
LiveBeansModel liveBeans = bootApp.getBeans();
|
||||
if (liveBeans != null && !liveBeans.isEmpty()) {
|
||||
addLiveHoverContent(annotation, doc, liveBeans, bootApp, hoverContent);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
if (hoverContent.size() > 0) {
|
||||
Range hoverRange = doc.toRange(annotation.getStartPosition(), annotation.getLength());
|
||||
Hover hover = new Hover();
|
||||
|
||||
hover.setContents(hoverContent);
|
||||
hover.setRange(hoverRange);
|
||||
|
||||
return CompletableFuture.completedFuture(hover);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Range> getLiveHoverHints(Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) {
|
||||
try {
|
||||
@@ -96,7 +53,7 @@ public class AutowiredHoverProvider implements HoverProvider {
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.log(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -109,12 +66,15 @@ public class AutowiredHoverProvider implements HoverProvider {
|
||||
|
||||
public Range getLiveHoverHint(Annotation annotation, TextDocument doc, LiveBeansModel beansModel) {
|
||||
try {
|
||||
String type = findDeclaredType(annotation);
|
||||
if (type != null && beansModel != null) {
|
||||
List<LiveBean> beansOfType = beansModel.getBeansOfType(type);
|
||||
if (!beansOfType.isEmpty()) {
|
||||
Range hoverRange = doc.toRange(annotation.getStartPosition(), annotation.getLength());
|
||||
return hoverRange;
|
||||
TypeDeclaration declaringType = ASTUtils.findDeclaringType(annotation);
|
||||
if (declaringType != null) {
|
||||
String type = declaringType.resolveBinding().getQualifiedName();
|
||||
if (type != null && beansModel != null) {
|
||||
List<LiveBean> beansOfType = beansModel.getBeansOfType(type);
|
||||
if (!beansOfType.isEmpty()) {
|
||||
Range hoverRange = doc.toRange(annotation.getStartPosition(), annotation.getLength());
|
||||
return hoverRange;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -125,53 +85,68 @@ public class AutowiredHoverProvider implements HoverProvider {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void addLiveHoverContent(Annotation annotation, TextDocument doc, LiveBeansModel beansModel, SpringBootAppProvider bootApp, List<Either<String, MarkedString>> hoverContent) {
|
||||
String type = findDeclaredType(annotation);
|
||||
if (type != null && beansModel != null) {
|
||||
List<LiveBean> beansOfType = beansModel.getBeansOfType(type);
|
||||
@Override
|
||||
public CompletableFuture<Hover> provideHover(ASTNode node, Annotation annotation, ITypeBinding type, int offset,
|
||||
TextDocument doc, SpringBootApp[] runningApps) {
|
||||
if (runningApps.length > 0) {
|
||||
|
||||
if (!beansOfType.isEmpty()) {
|
||||
String processId = bootApp.getProcessID();
|
||||
String processName = bootApp.getProcessName();
|
||||
StringBuilder hover = new StringBuilder();
|
||||
|
||||
for (LiveBean liveBean : beansOfType) {
|
||||
String[] dependencies = liveBean.getDependencies();
|
||||
LiveBean definedBean = ASTUtils.getDefinedBean(annotation);
|
||||
if (definedBean != null) {
|
||||
|
||||
if (dependencies != null && dependencies.length > 0) {
|
||||
hoverContent.add(Either.forLeft("bean: " + liveBean.getId()));
|
||||
hoverContent.add(Either.forLeft("injected beans:"));
|
||||
hover.append("**Injection report for " + LiveHoverUtils.showBean(definedBean) + "**\n\n");
|
||||
|
||||
for (String dependency : dependencies) {
|
||||
List<LiveBean> dependencyBeans = beansModel.getBeansOfName(dependency);
|
||||
for (LiveBean dependencyBean : dependencyBeans) {
|
||||
hoverContent.add(Either.forLeft("- '" + dependencyBean.getId() + "' - from: " + dependencyBean.getResource()));
|
||||
}
|
||||
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) {
|
||||
hover.append("\n\n");
|
||||
addAutomaticallyWired(hover, annotation, beans, bean);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// TODO: no dependencies found
|
||||
}
|
||||
}
|
||||
if (hasInterestingApp) {
|
||||
System.out.println(hover);
|
||||
return CompletableFuture
|
||||
.completedFuture(new Hover(ImmutableList.of(Either.forLeft(hover.toString()))));
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
hoverContent.add(Either.forLeft("Process ID: " + processId));
|
||||
hoverContent.add(Either.forLeft("Process Name: " + processName));
|
||||
private void addAutomaticallyWired(StringBuilder hover, Annotation annotation, LiveBeansModel beans, LiveBean bean) {
|
||||
TypeDeclaration typeDecl = ASTUtils.findDeclaringType(annotation);
|
||||
if (typeDecl != null) {
|
||||
String[] dependencies = bean.getDependencies();
|
||||
|
||||
if (dependencies != null && dependencies.length > 0) {
|
||||
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.showBean(dependencyBean));
|
||||
}
|
||||
firstDependency = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String findDeclaredType(Annotation annotation) {
|
||||
ASTNode node = annotation;
|
||||
while (node != null && !(node instanceof TypeDeclaration)) {
|
||||
node = node.getParent();
|
||||
}
|
||||
|
||||
if (node != null) {
|
||||
TypeDeclaration typeDecl = (TypeDeclaration) node;
|
||||
return typeDecl.resolveBinding().getQualifiedName();
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.autowired;
|
||||
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBeansModel;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public interface SpringBootAppProvider {
|
||||
|
||||
public LiveBeansModel getBeans() throws Exception;
|
||||
public String getProcessID();
|
||||
public String getProcessName();
|
||||
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.autowired;
|
||||
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBeansModel;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class SpringBootAppProviderImpl implements SpringBootAppProvider {
|
||||
|
||||
private SpringBootApp bootApp;
|
||||
|
||||
public SpringBootAppProviderImpl(SpringBootApp bootApp) {
|
||||
this.bootApp = bootApp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LiveBeansModel getBeans() throws Exception {
|
||||
return bootApp.getBeans();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProcessID() {
|
||||
return bootApp.getProcessID();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProcessName() {
|
||||
return bootApp.getProcessName();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -29,11 +29,11 @@ import org.eclipse.lsp4j.jsonrpc.messages.Either;
|
||||
import org.springframework.ide.vscode.boot.java.autowired.Constants;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.HoverProvider;
|
||||
import org.springframework.ide.vscode.boot.java.livehover.ASTUtils;
|
||||
import org.springframework.ide.vscode.boot.java.livehover.LiveHoverUtils;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBeansModel;
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
import org.springframework.ide.vscode.commons.util.StringUtil;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -45,9 +45,9 @@ public class ComponentInjectionsHoverProvider implements HoverProvider {
|
||||
// Highlight if any running app contains an instance of this component
|
||||
try {
|
||||
if (runningApps.length > 0) {
|
||||
LiveBean definedBean = getDefinedBean(annotation);
|
||||
LiveBean definedBean = ASTUtils.getDefinedBean(annotation);
|
||||
if (definedBean != null) {
|
||||
if (Stream.of(runningApps).anyMatch(app -> hasRelevantBeans(app, definedBean))) {
|
||||
if (Stream.of(runningApps).anyMatch(app -> LiveHoverUtils.hasRelevantBeans(app, definedBean))) {
|
||||
Optional<Range> nameRange = ASTUtils.nameRange(doc, annotation);
|
||||
if (nameRange.isPresent()) {
|
||||
return ImmutableList.of(nameRange.get());
|
||||
@@ -61,40 +61,20 @@ public class ComponentInjectionsHoverProvider implements HoverProvider {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
private boolean hasRelevantBeans(SpringBootApp app, LiveBean definedBean) {
|
||||
return findRelevantBeans(app, definedBean).findAny().isPresent();
|
||||
}
|
||||
|
||||
private Stream<LiveBean> findRelevantBeans(SpringBootApp app, LiveBean definedBean) {
|
||||
return app.getBeans().getBeansOfName(definedBean.getId()).stream()
|
||||
.filter(bean -> definedBean.getType().equals(bean.getType()));
|
||||
}
|
||||
|
||||
private LiveBean getDefinedBean(Annotation annotation) {
|
||||
ITypeBinding beanType = getAnnotatedType(annotation);
|
||||
if (beanType != null) {
|
||||
String id = getBeanId(annotation, beanType);
|
||||
if (StringUtil.hasText(id)) {
|
||||
return LiveBean.builder().id(id).type(beanType.getQualifiedName()).build();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Hover> provideHover(ASTNode node, Annotation annotation, ITypeBinding type, int offset,
|
||||
TextDocument doc, SpringBootApp[] runningApps) {
|
||||
if (runningApps.length > 0) {
|
||||
|
||||
LiveBean definedBean = getDefinedBean(annotation);
|
||||
LiveBean definedBean = ASTUtils.getDefinedBean(annotation);
|
||||
if (definedBean != null) {
|
||||
StringBuilder hover = new StringBuilder();
|
||||
hover.append("**Injection report for " + showBean(definedBean) + "**\n\n");
|
||||
hover.append("**Injection report for " + LiveHoverUtils.showBean(definedBean) + "**\n\n");
|
||||
|
||||
boolean hasInterestingApp = false;
|
||||
for (SpringBootApp app : runningApps) {
|
||||
LiveBeansModel beans = app.getBeans();
|
||||
List<LiveBean> relevantBeans = findRelevantBeans(app, definedBean).collect(Collectors.toList());
|
||||
List<LiveBean> relevantBeans = LiveHoverUtils.findRelevantBeans(app, definedBean).collect(Collectors.toList());
|
||||
|
||||
if (!relevantBeans.isEmpty()) {
|
||||
if (!hasInterestingApp) {
|
||||
@@ -102,7 +82,7 @@ public class ComponentInjectionsHoverProvider implements HoverProvider {
|
||||
} else {
|
||||
hover.append("\n\n");
|
||||
}
|
||||
hover.append(niceAppName(app) + ":");
|
||||
hover.append(LiveHoverUtils.niceAppName(app) + ":");
|
||||
|
||||
for (LiveBean bean : relevantBeans) {
|
||||
addInjectedInto(definedBean, hover, beans, bean);
|
||||
@@ -124,15 +104,15 @@ public class ComponentInjectionsHoverProvider implements HoverProvider {
|
||||
hover.append("\n\n");
|
||||
List<LiveBean> dependers = beans.getBeansDependingOn(bean.getId());
|
||||
if (dependers.isEmpty()) {
|
||||
hover.append(showBean(bean) + " exists but is **Not injected anywhere**\n");
|
||||
hover.append(LiveHoverUtils.showBean(bean) + " exists but is **Not injected anywhere**\n");
|
||||
} else {
|
||||
hover.append(showBean(definedBean) + " injected into:\n\n");
|
||||
hover.append(LiveHoverUtils.showBean(definedBean) + " injected into:\n\n");
|
||||
boolean firstDependency = true;
|
||||
for (LiveBean dependingBean : dependers) {
|
||||
if (!firstDependency) {
|
||||
hover.append("\n");
|
||||
}
|
||||
hover.append("- " + showBean(dependingBean));
|
||||
hover.append("- " + LiveHoverUtils.showBean(dependingBean));
|
||||
firstDependency = false;
|
||||
}
|
||||
}
|
||||
@@ -147,7 +127,7 @@ public class ComponentInjectionsHoverProvider implements HoverProvider {
|
||||
String[] dependencies = bean.getDependencies();
|
||||
|
||||
if (dependencies != null && dependencies.length > 0) {
|
||||
hover.append(showBean(bean) + " got autowired with:\n\n");
|
||||
hover.append(LiveHoverUtils.showBean(bean) + " got autowired with:\n\n");
|
||||
|
||||
boolean firstDependency = true;
|
||||
for (String injectedBean : dependencies) {
|
||||
@@ -156,7 +136,7 @@ public class ComponentInjectionsHoverProvider implements HoverProvider {
|
||||
}
|
||||
List<LiveBean> dependencyBeans = beans.getBeansOfName(injectedBean);
|
||||
for (LiveBean dependencyBean : dependencyBeans) {
|
||||
hover.append("- " + showBean(dependencyBean));
|
||||
hover.append("- " + LiveHoverUtils.showBean(dependencyBean));
|
||||
}
|
||||
firstDependency = false;
|
||||
}
|
||||
@@ -165,35 +145,6 @@ public class ComponentInjectionsHoverProvider implements HoverProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private String getBeanId(Annotation annotation, ITypeBinding beanType) {
|
||||
Optional<String> explicitId = ASTUtils.getValueAttribute(annotation);
|
||||
if (explicitId.isPresent()) {
|
||||
return explicitId.get();
|
||||
}
|
||||
String typeName = beanType.getName();
|
||||
if (StringUtil.hasText(typeName)) {
|
||||
return Character.toLowerCase(typeName.charAt(0)) + typeName.substring(1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String showBean(LiveBean bean) {
|
||||
return "Bean [id: " + bean.getId() + ", type: `" + bean.getType() + "`]";
|
||||
}
|
||||
|
||||
private ITypeBinding getAnnotatedType(Annotation annotation) {
|
||||
ASTNode parent = annotation.getParent();
|
||||
if (parent instanceof TypeDeclaration) {
|
||||
TypeDeclaration typeDecl = (TypeDeclaration) parent;
|
||||
return typeDecl.resolveBinding();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String niceAppName(SpringBootApp app) {
|
||||
return "Process [PID=" + app.getProcessID() + ", name=`" + app.getProcessName() + "`]";
|
||||
}
|
||||
|
||||
private boolean hasAutowiredAnnotation(MethodDeclaration constructor) {
|
||||
List<?> modifiers = constructor.modifiers();
|
||||
for (Object modifier : modifiers) {
|
||||
|
||||
@@ -17,12 +17,15 @@ import java.util.Optional;
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.Expression;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.MethodDeclaration;
|
||||
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
|
||||
import org.eclipse.jdt.core.dom.StringLiteral;
|
||||
import org.eclipse.jdt.core.dom.TypeDeclaration;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean;
|
||||
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;
|
||||
|
||||
public class ASTUtils {
|
||||
@@ -75,4 +78,31 @@ public class ASTUtils {
|
||||
|
||||
return constructors.toArray(new MethodDeclaration[constructors.size()]);
|
||||
}
|
||||
|
||||
public static LiveBean getDefinedBean(Annotation annotation) {
|
||||
TypeDeclaration declaringType = ASTUtils.findDeclaringType(annotation);
|
||||
if (declaringType != null) {
|
||||
ITypeBinding beanType = declaringType.resolveBinding();
|
||||
if (beanType != null) {
|
||||
String id = getBeanId(annotation, beanType);
|
||||
if (StringUtil.hasText(id)) {
|
||||
return LiveBean.builder().id(id).type(beanType.getQualifiedName()).build();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getBeanId(Annotation annotation, ITypeBinding beanType) {
|
||||
Optional<String> explicitId = ASTUtils.getValueAttribute(annotation);
|
||||
if (explicitId.isPresent()) {
|
||||
return explicitId.get();
|
||||
}
|
||||
String typeName = beanType.getName();
|
||||
if (StringUtil.hasText(typeName)) {
|
||||
return Character.toLowerCase(typeName.charAt(0)) + typeName.substring(1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.livehover;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean;
|
||||
|
||||
public class LiveHoverUtils {
|
||||
|
||||
public static String showBean(LiveBean bean) {
|
||||
return "Bean [id: " + bean.getId() + ", type: `" + bean.getType() + "`]";
|
||||
}
|
||||
|
||||
public static String niceAppName(SpringBootApp app) {
|
||||
return "Process [PID=" + app.getProcessID() + ", name=`" + app.getProcessName() + "`]";
|
||||
}
|
||||
|
||||
public static boolean hasRelevantBeans(SpringBootApp app, LiveBean definedBean) {
|
||||
return findRelevantBeans(app, definedBean).findAny().isPresent();
|
||||
}
|
||||
|
||||
public static Stream<LiveBean> findRelevantBeans(SpringBootApp app, LiveBean definedBean) {
|
||||
return app.getBeans().getBeansOfName(definedBean.getId()).stream()
|
||||
.filter(bean -> definedBean.getType().equals(bean.getType()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,219 +10,188 @@
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.autowired.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Stream;
|
||||
import java.time.Duration;
|
||||
|
||||
import org.eclipse.jdt.core.JavaCore;
|
||||
import org.eclipse.jdt.core.dom.AST;
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.ASTParser;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.CompilationUnit;
|
||||
import org.eclipse.jdt.core.dom.NodeFinder;
|
||||
import org.eclipse.lsp4j.Hover;
|
||||
import org.eclipse.lsp4j.MarkedString;
|
||||
import org.eclipse.lsp4j.Position;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
||||
import org.eclipse.lsp4j.jsonrpc.messages.Either;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.ide.vscode.boot.java.autowired.AutowiredHoverProvider;
|
||||
import org.springframework.ide.vscode.boot.java.autowired.SpringBootAppProvider;
|
||||
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.IClasspath;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
import org.springframework.ide.vscode.commons.util.BadLocationException;
|
||||
import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.Editor;
|
||||
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness;
|
||||
import org.springframework.ide.vscode.project.harness.MockRunningAppProvider;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness.CustomizableProjectContent;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness.ProjectCustomizer;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class AutowiredHoverProviderTest {
|
||||
|
||||
private static final ProjectCustomizer FOO_INTERFACE = (CustomizableProjectContent p) -> {
|
||||
p.createType("com.examle.Foo",
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"public interface Foo {\n" +
|
||||
" void doSomeFoo();\n" +
|
||||
"}\n"
|
||||
);
|
||||
|
||||
p.createType("com.examle.DependencyA",
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"public class DependencyA {\n" +
|
||||
"}\n"
|
||||
);
|
||||
|
||||
p.createType("com.examle.DependencyB",
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"public class DependencyB {\n" +
|
||||
"}\n"
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
private BootLanguageServerHarness harness;
|
||||
private JavaProjectFinder projectFinder;
|
||||
private ProjectsHarness projects = ProjectsHarness.INSTANCE;
|
||||
|
||||
private MockRunningAppProvider mockAppProvider;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
harness = BootLanguageServerHarness.builder().build();
|
||||
projectFinder = harness.getProjectFinder();
|
||||
mockAppProvider = new MockRunningAppProvider();
|
||||
harness = BootLanguageServerHarness.builder()
|
||||
.mockDefaults()
|
||||
.runningAppProvider(mockAppProvider.provider)
|
||||
.watchDogInterval(Duration.ofMillis(100))
|
||||
.build();
|
||||
|
||||
MavenJavaProject jp = projects.mavenProject("empty-boot-15-web-app", FOO_INTERFACE);
|
||||
assertTrue(jp.getClasspath().findType("com.example.Foo").exists());
|
||||
harness.useProject(projects.mavenProject("empty-boot-15-web-app"));
|
||||
harness.intialize(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLiveHoverHintForAutowiredOnConstructor() throws Exception {
|
||||
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-autowired/").toURI());
|
||||
harness.intialize(directory);
|
||||
public void componentWithAutomaticallyWiredConstructorInjections() throws Exception {
|
||||
LiveBeansModel beans = LiveBeansModel.builder()
|
||||
.add(LiveBean.builder()
|
||||
.id("autowiredClass")
|
||||
.type("com.example.AutowiredClass")
|
||||
.dependencies("dependencyA", "dependencyB")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("dependencyA")
|
||||
.type("com.example.DependencyA")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("dependencyB")
|
||||
.type("com.example.DependencyB")
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.processId("111")
|
||||
.processName("the-app")
|
||||
.beans(beans)
|
||||
.build();
|
||||
|
||||
String docURI = "file://" + directory.getAbsolutePath() + "/src/main/java/org/test/MyAutowiredComponent.java";
|
||||
TextDocument document = createTempTextDocument(docURI);
|
||||
IJavaProject project = projectFinder.find(new TextDocumentIdentifier(docURI)).get();
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.beans.factory.annotation.Autowired;\n" +
|
||||
"import org.springframework.stereotype.Component;\n" +
|
||||
"\n" +
|
||||
"@Component\n" +
|
||||
"public class AutowiredClass {\n" +
|
||||
"\n" +
|
||||
" @Autowired\n" +
|
||||
" public AutowiredClass(DependencyA depA, DependencyB depB) {\n" +
|
||||
" }\n" +
|
||||
"}\n"
|
||||
);
|
||||
|
||||
CompilationUnit cu = parse(document, project);
|
||||
|
||||
int offset = document.toOffset(new Position(11, 4));
|
||||
ASTNode node = NodeFinder.perform(cu, offset, 0).getParent();
|
||||
|
||||
AutowiredHoverProvider provider = new AutowiredHoverProvider();
|
||||
String beansJSON = new String(Files.readAllBytes(new File(directory, "runtime-bean-information.json").toPath()));
|
||||
|
||||
Range hint = provider.getLiveHoverHint((Annotation)node, document, LiveBeansModel.parse(beansJSON));
|
||||
assertNotNull(hint);
|
||||
|
||||
assertEquals(11, hint.getStart().getLine());
|
||||
assertEquals(1, hint.getStart().getCharacter());
|
||||
assertEquals(11, hint.getEnd().getLine());
|
||||
assertEquals(11, hint.getEnd().getCharacter());
|
||||
editor.assertHighlights("@Component", "@Autowired");
|
||||
editor.assertTrimmedHover("@Autowired",
|
||||
"**Injection report for Bean [id: autowiredClass, type: `com.example.AutowiredClass`]**\n" +
|
||||
"\n" +
|
||||
"Process [PID=111, name=`the-app`]:\n" +
|
||||
"\n" +
|
||||
"Bean [id: autowiredClass, type: `com.example.AutowiredClass`] got autowired with:\n" +
|
||||
"\n" +
|
||||
"- Bean [id: dependencyA, type: `com.example.DependencyA`]\n" +
|
||||
"- Bean [id: dependencyB, type: `com.example.DependencyB`]\n"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoLiveHoverHintForAutowiredOnConstructorWithNoLiveAppData() throws Exception {
|
||||
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-autowired/").toURI());
|
||||
harness.intialize(directory);
|
||||
public void noHoversWhenNoRunningApps() throws Exception {
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.beans.factory.annotation.Autowired;\n" +
|
||||
"import org.springframework.stereotype.Component;\n" +
|
||||
"\n" +
|
||||
"@Component\n" +
|
||||
"public class AutowiredClass {\n" +
|
||||
"\n" +
|
||||
" @Autowired\n" +
|
||||
" public AutowiredClass(DependencyA depA, DependencyB depB) {\n" +
|
||||
" }\n" +
|
||||
"}\n"
|
||||
);
|
||||
|
||||
String docURI = "file://" + directory.getAbsolutePath() + "/src/main/java/org/test/MyAutowiredComponent.java";
|
||||
TextDocument document = createTempTextDocument(docURI);
|
||||
IJavaProject project = projectFinder.find(new TextDocumentIdentifier(docURI)).get();
|
||||
|
||||
CompilationUnit cu = parse(document, project);
|
||||
|
||||
int offset = document.toOffset(new Position(11, 4));
|
||||
ASTNode node = NodeFinder.perform(cu, offset, 0).getParent();
|
||||
|
||||
AutowiredHoverProvider provider = new AutowiredHoverProvider();
|
||||
Range hint = provider.getLiveHoverHint((Annotation)node, document, LiveBeansModel.parse(null));
|
||||
assertNull(hint);
|
||||
editor.assertHighlights(/*MONE*/);
|
||||
editor.assertNoHover("@Autowired");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoLiveHoverHintForAutowiredOnConstructorWithWrongLiveAppData() throws Exception {
|
||||
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-autowired/").toURI());
|
||||
harness.intialize(directory);
|
||||
public void noHoversWhenRunningAppDoesntHaveTheComponent() throws Exception {
|
||||
LiveBeansModel beans = LiveBeansModel.builder()
|
||||
.add(LiveBean.builder()
|
||||
.id("whateverBean")
|
||||
.type("com.example.UnrelatedComponent")
|
||||
.build()
|
||||
)
|
||||
.add(LiveBean.builder()
|
||||
.id("myController")
|
||||
.type("com.example.UnrelatedComponent")
|
||||
.dependencies("whateverBean")
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
mockAppProvider.builder()
|
||||
.isSpringBootApp(true)
|
||||
.processId("111")
|
||||
.processName("unrelated-app")
|
||||
.beans(beans)
|
||||
.build();
|
||||
|
||||
String docURI = "file://" + directory.getAbsolutePath() + "/src/main/java/org/test/MyAutowiredComponent.java";
|
||||
TextDocument document = createTempTextDocument(docURI);
|
||||
IJavaProject project = projectFinder.find(new TextDocumentIdentifier(docURI)).get();
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA,
|
||||
"package com.example;\n" +
|
||||
"\n" +
|
||||
"import org.springframework.beans.factory.annotation.Autowired;\n" +
|
||||
"import org.springframework.stereotype.Component;\n" +
|
||||
"\n" +
|
||||
"@Component\n" +
|
||||
"public class AutowiredClass {\n" +
|
||||
"\n" +
|
||||
" @Autowired\n" +
|
||||
" public AutowiredClass(DependencyA depA, DependencyB depB) {\n" +
|
||||
" }\n" +
|
||||
"}\n"
|
||||
);
|
||||
|
||||
CompilationUnit cu = parse(document, project);
|
||||
|
||||
int offset = document.toOffset(new Position(11, 4));
|
||||
ASTNode node = NodeFinder.perform(cu, offset, 0).getParent();
|
||||
|
||||
AutowiredHoverProvider provider = new AutowiredHoverProvider();
|
||||
String beansJSON = new String(Files.readAllBytes(new File(directory, "wrong-runtime-bean-information.json").toPath()));
|
||||
|
||||
Range hint = provider.getLiveHoverHint((Annotation)node, document, LiveBeansModel.parse(beansJSON));
|
||||
assertNull(hint);
|
||||
editor.assertHighlights(/*MONE*/);
|
||||
editor.assertNoHover("@Autowired");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLiveHoverContentForAutowiredOnConstructor() throws Exception {
|
||||
File directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-autowired/").toURI());
|
||||
harness.intialize(directory);
|
||||
|
||||
String docURI = "file://" + directory.getAbsolutePath() + "/src/main/java/org/test/MyAutowiredComponent.java";
|
||||
TextDocument document = createTempTextDocument(docURI);
|
||||
IJavaProject project = projectFinder.find(new TextDocumentIdentifier(docURI)).get();
|
||||
|
||||
CompilationUnit cu = parse(document, project);
|
||||
|
||||
int offset = document.toOffset(new Position(11, 4));
|
||||
ASTNode node = NodeFinder.perform(cu, offset, 0).getParent();
|
||||
|
||||
AutowiredHoverProvider provider = new AutowiredHoverProvider();
|
||||
LiveBeansModel beansModel = LiveBeansModel.parse(new String(Files.readAllBytes(new File(directory, "runtime-bean-information.json").toPath())));
|
||||
|
||||
SpringBootAppProvider bootApp = new SpringBootAppProvider() {
|
||||
@Override
|
||||
public String getProcessName() {
|
||||
return "test process name";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProcessID() {
|
||||
return "test process id";
|
||||
}
|
||||
|
||||
@Override
|
||||
public LiveBeansModel getBeans() throws Exception {
|
||||
return beansModel;
|
||||
}
|
||||
};
|
||||
CompletableFuture<Hover> hoverFuture = provider.provideHover(null, (Annotation)node, null, offset, document, new SpringBootAppProvider[] {bootApp});
|
||||
Hover hover = hoverFuture.get();
|
||||
assertNotNull(hover);
|
||||
|
||||
assertEquals(11, hover.getRange().getStart().getLine());
|
||||
assertEquals(1, hover.getRange().getStart().getCharacter());
|
||||
assertEquals(11, hover.getRange().getEnd().getLine());
|
||||
assertEquals(11, hover.getRange().getEnd().getCharacter());
|
||||
|
||||
List<Either<String, MarkedString>> contents = hover.getContents();
|
||||
assertEquals(6, contents.size());
|
||||
|
||||
assertTrue(contents.get(0).getLeft().contains("myAutowiredComponent"));
|
||||
assertTrue(contents.get(2).getLeft().contains("dependencyA"));
|
||||
assertTrue(contents.get(3).getLeft().contains("dependencyB"));
|
||||
assertTrue(contents.get(4).getLeft().contains("test process id"));
|
||||
assertTrue(contents.get(5).getLeft().contains("test process name"));
|
||||
}
|
||||
|
||||
private TextDocument createTempTextDocument(String docURI) throws Exception {
|
||||
Path path = Paths.get(new URI(docURI));
|
||||
String content = new String(Files.readAllBytes(path));
|
||||
|
||||
TextDocument doc = new TextDocument(docURI, LanguageId.PLAINTEXT, 0, content);
|
||||
return doc;
|
||||
}
|
||||
|
||||
private CompilationUnit parse(TextDocument document, IJavaProject project)
|
||||
throws Exception, BadLocationException {
|
||||
ASTParser parser = ASTParser.newParser(AST.JLS8);
|
||||
Map<String, String> options = JavaCore.getOptions();
|
||||
JavaCore.setComplianceOptions(JavaCore.VERSION_1_8, options);
|
||||
parser.setCompilerOptions(options);
|
||||
parser.setKind(ASTParser.K_COMPILATION_UNIT);
|
||||
parser.setStatementsRecovery(true);
|
||||
parser.setBindingsRecovery(true);
|
||||
parser.setResolveBindings(true);
|
||||
|
||||
String[] classpathEntries = getClasspathEntries(project);
|
||||
String[] sourceEntries = new String[] {};
|
||||
parser.setEnvironment(classpathEntries, sourceEntries, null, true);
|
||||
|
||||
String docURI = document.getUri();
|
||||
String unitName = docURI.substring(docURI.lastIndexOf("/"));
|
||||
parser.setUnitName(unitName);
|
||||
parser.setSource(document.get(0, document.getLength()).toCharArray());
|
||||
|
||||
CompilationUnit cu = (CompilationUnit) parser.createAST(null);
|
||||
return cu;
|
||||
}
|
||||
|
||||
private String[] getClasspathEntries(IJavaProject project) throws Exception {
|
||||
IClasspath classpath = project.getClasspath();
|
||||
Stream<Path> classpathEntries = classpath.getClasspathEntries();
|
||||
return classpathEntries
|
||||
.filter(path -> path.toFile().exists())
|
||||
.map(path -> path.toAbsolutePath().toString()).toArray(String[]::new);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user