Skip to content

Commit

Permalink
PT #159371605: Implicitly autowired constructor boot hint and hover
Browse files Browse the repository at this point in the history
  • Loading branch information
BoykoAlex committed Jul 28, 2018
1 parent 0d4b6ee commit 705d81f
Show file tree
Hide file tree
Showing 14 changed files with 397 additions and 252 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -600,18 +600,26 @@ public void assertHoverContains(String hoverOver, String snippet) throws Excepti
}

public void assertTrimmedHover(String hoverOver, String expectedHover) throws Exception {
int hoverPosition = getHoverPosition(hoverOver,1);
assertTrimmedHover(hoverOver, 1, expectedHover);
}

public void assertTrimmedHover(String hoverOver, int occurence, String expectedHover) throws Exception {
int hoverPosition = getHoverPosition(hoverOver,occurence);
Hover hover = harness.getHover(doc, doc.toPosition(hoverPosition));
assertEquals(expectedHover.trim(), hoverString(hover).trim());
}

public void assertNoHover(String hoverOver) throws Exception {
int hoverPosition = getRawText().indexOf(hoverOver) + hoverOver.length() / 2;
public void assertNoHover(String hoverOver, int occurence) throws Exception {
int hoverPosition = getHoverPosition(hoverOver,occurence);
Hover hover = harness.getHover(doc, doc.toPosition(hoverPosition));
List<Either<String, MarkedString>> contents = hover.getContents().getLeft();
assertTrue(contents.toString(), contents.isEmpty());
}

public void assertNoHover(String hoverOver) throws Exception {
assertNoHover(hoverOver, 1);
}

/**
* Verifies an expected textSnippet is contained in the hover text that is
* computed when hovering mouse at position at the end of first occurrence of
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
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;
Expand All @@ -40,6 +41,7 @@
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;

Expand All @@ -63,109 +65,116 @@ public AutowiredHoverProvider(BootJavaLanguageServerComponents server) {
}

@Override
public Collection<Range> getLiveHoverHints(Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) {
public Collection<Range> getLiveHoverHints(IJavaProject project, Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) {
LiveBean definedBean = getDefinedBeanForTypeDeclaration(ASTUtils.findDeclaringType(annotation));
// Annotation is MarkerNode, parent is some field, method, variable declaration node.
ASTNode declarationNode = annotation.getParent();
try {
LiveBean definedBean = getDefinedBean(annotation);
if (definedBean != null) {
for (SpringBootApp app : runningApps) {
try {
List<LiveBean> relevantBeans = LiveHoverUtils.findRelevantBeans(app, definedBean).collect(Collectors.toList());

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);
}
}
}
}
catch (Exception e) {
Range hoverRange = doc.toRange(annotation.getStartPosition(), annotation.getLength());
return getLiveHoverHints(project, declarationNode, hoverRange, runningApps, definedBean);
} catch (BadLocationException e) {
log.error("", e);
}
return null;
}

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);
}
}
}
return null;
}

@Override
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);
}

private Hover provideHover(LiveBean definedBean, ASTNode declarationNode, int offset, TextDocument doc,
IJavaProject project, SpringBootApp[] runningApps) {
if (definedBean != null && runningApps.length > 0) {

StringBuilder hover = new StringBuilder();

LiveBean definedBean = getDefinedBean(annotation);
if (definedBean != null) {

boolean hasContent = false;

for (SpringBootApp app : runningApps) {
LiveBeansModel beans = app.getBeans();
List<LiveBean> relevantBeans = LiveHoverUtils.findRelevantBeans(app, definedBean).collect(Collectors.toList());

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");
}
boolean hasContent = false;

for (SpringBootApp app : runningApps) {

List<LiveBean> autowiredBeans = getRelevantAutowiredBeans(project, declarationNode, app, definedBean);

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.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));
}
}
}

}
if (hasContent) {
return new Hover(ImmutableList.of(Either.forLeft(hover.toString())));
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())));
}
}
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)
.collect(Collectors.toList());

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) {
Expand Down Expand Up @@ -198,6 +207,9 @@ private List<LiveBean> matchBeans(IJavaProject project, Collection<LiveBean> bea
.map(subType -> matchBeans(project, beans, subType.getFullyQualifiedName()))
.filter(relevantBeans -> !relevantBeans.isEmpty())
.blockFirst();
if (relevant == null) {
relevant = Collections.emptyList();
}
}
}
}
Expand All @@ -213,40 +225,71 @@ private List<LiveBean> matchBeans(IJavaProject project, Collection<LiveBean> bea
}
}

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;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
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;
Expand All @@ -50,7 +49,7 @@ public Hover provideHover(ASTNode node, Annotation annotation, ITypeBinding type
}

@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()) {
Expand Down Expand Up @@ -156,16 +155,4 @@ protected boolean matchesAnnotation(Annotation annotation, LiveConditional liveC
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;
}

}
Loading

0 comments on commit 705d81f

Please sign in to comment.