diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/ComponentInjectionsHoverProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/ComponentInjectionsHoverProvider.java index 8cc525b56..68aa4936f 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/ComponentInjectionsHoverProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/ComponentInjectionsHoverProvider.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017, 2018 Pivotal, Inc. + * Copyright (c) 2017, 2019 Pivotal, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -17,6 +17,7 @@ import java.util.Optional; import java.util.Set; import java.util.stream.Stream; +import org.eclipse.jdt.core.Flags; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.Annotation; import org.eclipse.jdt.core.dom.ITypeBinding; @@ -64,7 +65,7 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP if (declaringType != null) { ITypeBinding beanType = declaringType.resolveBinding(); if (beanType != null) { - String id = getBeanId(annotation, beanType); + String id = getBeanId(annotation, beanType, Flags.isStatic(declaringType.getModifiers())); if (StringUtil.hasText(id)) { return LiveBean.builder().id(id).type(getBeanType(beanType).toString()).build(); } @@ -73,30 +74,31 @@ public class ComponentInjectionsHoverProvider extends AbstractInjectedIntoHoverP return null; } - private static String getBeanId(Annotation annotation, ITypeBinding beanType) { - return ASTUtils.getAttribute(annotation, "value").flatMap(ASTUtils::getFirstString) - .orElseGet(() -> { - String typeName = beanType.getName(); - + private static String getBeanId(Annotation annotation, ITypeBinding beanType, boolean isStatic) { + return ASTUtils.getAttribute(annotation, "value").flatMap(ASTUtils::getFirstString).orElseGet(() -> { ITypeBinding declaringClass = beanType.getDeclaringClass(); - if (declaringClass != null) { - return getBeanType(beanType).toString(); + if (declaringClass == null) { + return BeanUtils.getBeanNameFromType(beanType.getName()); + } else { + if (isStatic) { + // Static inner class case id `outerClass.InnerClass` + String typeName = beanType.getBinaryName(); + // Trim package prefix and replace $ with . inner class separator + int idx = typeName.lastIndexOf('.'); + if (idx >= 0) { + typeName = typeName.substring(idx + 1).replace('$', '.'); + } + return BeanUtils.getBeanNameFromType(typeName); + } else { + // Non-static inner class id case is binary type name + return getBeanType(beanType).toString(); + } } - - return BeanUtils.getBeanNameFromType(typeName); }); } - 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; - } + private static String getBeanType(ITypeBinding beanType) { + return beanType.getBinaryName(); } @Override diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/ComponentInjectionsHoverProviderTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/ComponentInjectionsHoverProviderTest.java index 1d0356d28..e91ea2d97 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/ComponentInjectionsHoverProviderTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/ComponentInjectionsHoverProviderTest.java @@ -664,13 +664,18 @@ public class ComponentInjectionsHoverProviderTest { } @Test - public void componentFromInnerClass() throws Exception { + public void componentFromStaticInnerClass() throws Exception { LiveBeansModel beans = LiveBeansModel.builder() .add(LiveBean.builder() .id("com.example.DemoApplication$InnerClass") .type("com.example.DemoApplication$InnerClass") .build() ) + .add(LiveBean.builder() + .id("demoApplication.InnerClass") + .type("com.example.DemoApplication$InnerClass") + .build() + ) .build(); mockAppProvider.builder() .isSpringBootApp(true) @@ -700,19 +705,77 @@ public class ComponentInjectionsHoverProviderTest { ); editor.assertHighlights("@SpringBootApplication"); editor.assertHoverContains("@SpringBootApplication", - "Bean id: `com.example.DemoApplication$InnerClass` \n" + + "Bean id: `demoApplication.InnerClass` \n" + "Process [PID=111, name=`the-app`]" ); } @Test - public void componentFromInnerInnerClass() throws Exception { + public void componentFromNestedClass() throws Exception { + LiveBeansModel beans = LiveBeansModel.builder() + .add(LiveBean.builder() + .id("com.example.Example$Inner") + .type("com.example.Example$Inner") + .build() + ) + .add(LiveBean.builder() + .id("example.Inner") + .type("com.example.Example$Inner") + .build() + ) + .add(LiveBean.builder() + .id("example") + .type("com.example.Example") + .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 Example { \n" + + " @Component\n" + + " public class Inner {\n" + + " @Autowired\n" + + " public Inner() {\n" + + " }\n" + + " }\n" + + "}\n" + ); + editor.assertHighlights("@Component", "@Component"); + editor.assertHoverContains("@Component", 1, + "Bean id: `example` \n" + + "Process [PID=111, name=`the-app`]" + ); + editor.assertHoverContains("@Component", 2, + "Bean id: `com.example.Example$Inner` \n" + + "Process [PID=111, name=`the-app`]" + ); + } + + @Test + public void componentFromStaticInnerInnerClass() throws Exception { LiveBeansModel beans = LiveBeansModel.builder() .add(LiveBean.builder() .id("com.example.DemoApplication$InnerClass$InnerInnerClass") .type("com.example.DemoApplication$InnerClass$InnerInnerClass") .build() ) + .add(LiveBean.builder() + .id("demoApplication.InnerClass.InnerInnerClass") + .type("com.example.DemoApplication$InnerClass$InnerInnerClass") + .build() + ) .build(); mockAppProvider.builder() .isSpringBootApp(true) @@ -745,7 +808,7 @@ public class ComponentInjectionsHoverProviderTest { ); editor.assertHighlights("@SpringBootApplication"); editor.assertHoverContains("@SpringBootApplication", - "Bean id: `com.example.DemoApplication$InnerClass$InnerInnerClass` \n" + + "Bean id: `demoApplication.InnerClass.InnerInnerClass` \n" + "Process [PID=111, name=`the-app`]" ); }