firt rough cut to include conditional and profile annotations in bean symbols instead of their own ones

This commit is contained in:
Martin Lippert
2018-08-07 15:11:05 +02:00
parent 531656f88b
commit 64e7bbad45
10 changed files with 286 additions and 20 deletions

View File

@@ -55,6 +55,7 @@ import org.springframework.ide.vscode.boot.java.snippets.JavaSnippet;
import org.springframework.ide.vscode.boot.java.snippets.JavaSnippetContext;
import org.springframework.ide.vscode.boot.java.snippets.JavaSnippetManager;
import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache;
import org.springframework.ide.vscode.boot.java.utils.RestrictedDefaultSymbolProvider;
import org.springframework.ide.vscode.boot.java.utils.SpringIndexer;
import org.springframework.ide.vscode.boot.java.utils.SpringLiveChangeDetectionWatchdog;
import org.springframework.ide.vscode.boot.java.utils.SpringLiveHoverWatchdog;
@@ -344,6 +345,27 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent
providers.put(Annotations.BEAN, new BeansSymbolProvider());
providers.put(Annotations.COMPONENT, new ComponentSymbolProvider());
providers.put(Annotations.PROFILE, new RestrictedDefaultSymbolProvider());
providers.put(Annotations.CONDITIONAL, new RestrictedDefaultSymbolProvider());
providers.put(Annotations.CONDITIONAL_ON_BEAN, new RestrictedDefaultSymbolProvider());
providers.put(Annotations.CONDITIONAL_ON_MISSING_BEAN, new RestrictedDefaultSymbolProvider());
providers.put(Annotations.CONDITIONAL_ON_PROPERTY, new RestrictedDefaultSymbolProvider());
providers.put(Annotations.CONDITIONAL_ON_RESOURCE, new RestrictedDefaultSymbolProvider());
providers.put(Annotations.CONDITIONAL_ON_CLASS, new RestrictedDefaultSymbolProvider());
providers.put(Annotations.CONDITIONAL_ON_MISSING_CLASS, new RestrictedDefaultSymbolProvider());
providers.put(Annotations.CONDITIONAL_ON_CLOUD_PLATFORM, new RestrictedDefaultSymbolProvider());
providers.put(Annotations.CONDITIONAL_ON_WEB_APPLICATION, new RestrictedDefaultSymbolProvider());
providers.put(Annotations.CONDITIONAL_ON_NOT_WEB_APPLICATION, new RestrictedDefaultSymbolProvider());
providers.put(Annotations.CONDITIONAL_ON_ENABLED_INFO_CONTRIBUTOR, new RestrictedDefaultSymbolProvider());
providers.put(Annotations.CONDITIONAL_ON_ENABLED_RESOURCE_CHAIN, new RestrictedDefaultSymbolProvider());
providers.put(Annotations.CONDITIONAL_ON_ENABLED_ENDPOINT, new RestrictedDefaultSymbolProvider());
providers.put(Annotations.CONDITIONAL_ON_ENABLED_HEALTH_INDICATOR, new RestrictedDefaultSymbolProvider());
providers.put(Annotations.CONDITIONAL_ON_EXPRESSION, new RestrictedDefaultSymbolProvider());
providers.put(Annotations.CONDITIONAL_ON_JAVA, new RestrictedDefaultSymbolProvider());
providers.put(Annotations.CONDITIONAL_ON_JNDI, new RestrictedDefaultSymbolProvider());
providers.put(Annotations.CONDITIONAL_ON_SINGLE_CANDIDATE, new RestrictedDefaultSymbolProvider());
providers.put(Annotations.REPOSITORY, new DataRepositorySymbolProvider());
providers.put("", new WebfluxRouterSymbolProvider());

View File

@@ -15,6 +15,7 @@ import java.util.List;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.IAnnotationBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.Modifier;
@@ -25,6 +26,7 @@ import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.SymbolInformation;
import org.eclipse.lsp4j.SymbolKind;
import org.springframework.ide.vscode.boot.java.Annotations;
import org.springframework.ide.vscode.boot.java.handlers.EnhancedSymbolInformation;
import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider;
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
@@ -56,11 +58,12 @@ public class BeansSymbolProvider implements SymbolProvider {
boolean isFunction = isFunctionBean(node);
String beanType = getBeanType(node);
String markerString = getAnnotations(node);
for (Tuple2<String, DocumentRegion> nameAndRegion : getBeanNames(node, doc)) {
try {
symbols.add(new EnhancedSymbolInformation(
new SymbolInformation(
beanLabel(isFunction, nameAndRegion.getT1(), beanType, "@Bean"),
beanLabel(isFunction, nameAndRegion.getT1(), beanType, "@Bean" + markerString),
SymbolKind.Interface,
new Location(doc.getUri(), doc.toRange(nameAndRegion.getT2()))),
null
@@ -172,6 +175,31 @@ public class BeansSymbolProvider implements SymbolProvider {
return false;
}
private String getAnnotations(Annotation node) {
StringBuilder result = new StringBuilder();
ASTNode parent = node.getParent();
if (parent instanceof MethodDeclaration) {
MethodDeclaration method = (MethodDeclaration) parent;
List<?> modifiers = method.modifiers();
for (Object modifier : modifiers) {
if (modifier instanceof Annotation) {
Annotation annotation = (Annotation) modifier;
IAnnotationBinding annotationBinding = annotation.resolveAnnotationBinding();
String type = annotationBinding.getAnnotationType().getBinaryName();
if (type != null && !Annotations.BEAN.equals(type)) {
result.append(' ');
result.append(annotation.toString());
}
}
}
}
return result.toString();
}
private boolean isMethodAbstract(Annotation node) {
if (node != null && node.getParent() != null && node.getParent() instanceof MethodDeclaration) {
MethodDeclaration method = (MethodDeclaration) node.getParent();

View File

@@ -0,0 +1,30 @@
/*******************************************************************************
* Copyright (c) 2018 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.utils;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.SymbolInformation;
import org.eclipse.lsp4j.SymbolKind;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
/**
* @author Martin Lippert
*/
public class DefaultSymbolProvider {
public static SymbolInformation provideDefaultSymbol(Annotation node, TextDocument doc) throws Exception {
SymbolInformation symbol = new SymbolInformation(node.toString(), SymbolKind.Interface,
new Location(doc.getUri(), doc.toRange(node.getStartPosition(), node.getLength())));
return symbol;
}
}

View File

@@ -0,0 +1,87 @@
/*******************************************************************************
* Copyright (c) 2018 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.utils;
import java.util.Collection;
import java.util.List;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.IAnnotationBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.springframework.ide.vscode.boot.java.Annotations;
import org.springframework.ide.vscode.boot.java.handlers.EnhancedSymbolInformation;
import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider;
import org.springframework.ide.vscode.commons.util.Log;
import org.springframework.ide.vscode.commons.util.text.TextDocument;
import com.google.common.collect.ImmutableList;
/**
* @author Martin Lippert
*/
public class RestrictedDefaultSymbolProvider implements SymbolProvider {
@Override
public Collection<EnhancedSymbolInformation> getSymbols(Annotation node, ITypeBinding typeBinding,
Collection<ITypeBinding> metaAnnotations, TextDocument doc) {
// provide default symbol only in case this annotation is not combined with @Bean annotation
if (isCombinedWithAnnotation(node, Annotations.BEAN)) {
return null;
}
else {
ImmutableList.Builder<EnhancedSymbolInformation> symbols = ImmutableList.builder();
try {
symbols.add(new EnhancedSymbolInformation(DefaultSymbolProvider.provideDefaultSymbol(node, doc), null));
} catch (Exception e) {
Log.log(e);
}
return symbols.build();
}
}
private boolean isCombinedWithAnnotation(Annotation node, String annotation) {
ASTNode parent = node.getParent();
if (parent instanceof MethodDeclaration) {
MethodDeclaration method = (MethodDeclaration) parent;
List<?> modifiers = method.modifiers();
for (Object modifier : modifiers) {
if (modifier instanceof Annotation) {
Annotation anno = (Annotation) modifier;
IAnnotationBinding annotationBinding = anno.resolveAnnotationBinding();
String type = annotationBinding.getAnnotationType().getBinaryName();
if (type != null && type.equals(annotation)) {
return true;
}
}
}
}
return false;
}
@Override
public Collection<EnhancedSymbolInformation> getSymbols(TypeDeclaration typeDeclaration, TextDocument doc) {
return null;
}
@Override
public Collection<EnhancedSymbolInformation> getSymbols(MethodDeclaration methodDeclaration, TextDocument doc) {
return null;
}
}

View File

@@ -48,9 +48,7 @@ import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.SymbolInformation;
import org.eclipse.lsp4j.SymbolKind;
import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -517,9 +515,7 @@ public class SpringIndexer {
String qualifiedName = type.getQualifiedName();
if (qualifiedName != null && qualifiedName.startsWith("org.springframework")) {
TextDocument doc = getTempTextDocument(docURI, docRef, content);
SymbolInformation symbol = new SymbolInformation(node.toString(), SymbolKind.Interface,
new Location(doc.getUri(), doc.toRange(node.getStartPosition(), node.getLength())));
return symbol;
return DefaultSymbolProvider.provideDefaultSymbol(node, doc);
}
}
}

View File

@@ -44,7 +44,7 @@ public class SpringIndexerBeansTest {
harness = BootJavaLanguageServerHarness.builder().build();
harness.intialize(null);
indexer = harness.getServerWrapper().getComponents().getSpringIndexer();
directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-beans/").toURI());
@@ -90,6 +90,29 @@ public class SpringIndexerBeansTest {
);
}
@Test
public void testScanConfigurationClassWithConditionals() throws Exception {
String docUri = directory.toPath().resolve("src/main/java/org/test/ConfigurationWithConditionals.java").toUri().toString();
SpringIndexerHarness.assertDocumentSymbols(indexer, docUri,
SpringIndexerHarness.symbol("@Configuration", "@+ 'configurationWithConditionals' (@Configuration <: @Component) ConfigurationWithConditionals"),
SpringIndexerHarness.symbol("@Bean", "@+ 'conditionalBean' (@Bean @ConditionalOnJava(JavaVersion.EIGHT)) BeanClass"),
SpringIndexerHarness.symbol("@Bean", "@+ 'conditionalBeanDifferentSequence' (@Bean @ConditionalOnJava(JavaVersion.EIGHT)) BeanClass"),
SpringIndexerHarness.symbol("@Bean", "@+ 'conditionalBeanWithJavaAndCloud' (@Bean @ConditionalOnJava(JavaVersion.EIGHT) @Profile(\"cloud\")) BeanClass")
);
}
@Test
public void testScanConfigurationClassWithConditionalsDefaultSymbol() throws Exception {
String docUri = directory.toPath().resolve("src/main/java/org/test/ConfigurationWithConditionalsDefaultSymbols.java").toUri().toString();
SpringIndexerHarness.assertDocumentSymbols(indexer, docUri,
SpringIndexerHarness.symbol("@Configuration", "@+ 'configurationWithConditionalsDefaultSymbols' (@Configuration <: @Component) ConfigurationWithConditionalsDefaultSymbols"),
SpringIndexerHarness.symbol("@ConditionalOnJava(JavaVersion.EIGHT)", "@ConditionalOnJava(JavaVersion.EIGHT)"),
SpringIndexerHarness.symbol("@Profile(\"cloud\")", "@Profile(\"cloud\")"),
SpringIndexerHarness.symbol("@ConditionalOnJava(JavaVersion.EIGHT)", "@ConditionalOnJava(JavaVersion.EIGHT)"),
SpringIndexerHarness.symbol("@Profile(\"cloud\")", "@Profile(\"cloud\")")
);
}
@Test
public void testScanAbstractBeanConfiguration() throws Exception {
String docUri = directory.toPath().resolve("src/main/java/org/test/AbstractBeanConfiguration.java").toUri().toString();

View File

@@ -37,7 +37,7 @@ import org.springframework.ide.vscode.project.harness.ProjectsHarness;
* @author Martin Lippert
*/
public class SpringIndexerTest {
private BootJavaLanguageServerHarness harness;
private File directory;
private SpringIndexer indexer;
@@ -47,13 +47,13 @@ public class SpringIndexerTest {
@Before
public void setup() throws Exception {
harness = BootJavaLanguageServerHarness.builder().build();
harness.intialize(null);
indexer = harness.getServerWrapper().getComponents().getSpringIndexer();
directory = new File(ProjectsHarness.class.getResource("/test-projects/test-annotation-indexing-parent/test-annotation-indexing/").toURI());
projectDir = directory.toURI().toString();
// trigger project creation
project = harness.getServerWrapper().getComponents().getProjectFinder().find(new TextDocumentIdentifier(projectDir)).get();
@@ -65,7 +65,7 @@ public class SpringIndexerTest {
public void testScanningAllAnnotationsSimpleProjectUpfront() throws Exception {
List<? extends SymbolInformation> allSymbols = indexer.getAllSymbols("");
assertEquals(6, allSymbols.size());
assertEquals(7, allSymbols.size());
String docUri = directory.toPath().resolve("src/main/java/org/test/MainClass.java").toUri().toString();
assertTrue(containsSymbol(allSymbols, "@+ 'mainClass' (@SpringBootApplication <: @SpringBootConfiguration, @Configuration, @Component) MainClass", docUri, 6, 0, 6, 22));
@@ -78,6 +78,9 @@ public class SpringIndexerTest {
docUri = directory.toPath().resolve("src/main/java/org/test/sub/MappingClassSubpackage.java").toUri().toString();
assertTrue(containsSymbol(allSymbols, "@/classlevel/mapping-subpackage", docUri, 7, 1, 7, 38));
docUri = directory.toPath().resolve("src/main/java/org/test/ClassWithDefaultSymbol.java").toUri().toString();
assertTrue(containsSymbol(allSymbols, "@Configurable", docUri, 4, 0, 4, 13));
}
@Test
@@ -105,7 +108,7 @@ public class SpringIndexerTest {
public void testScanningAllAnnotationsMultiModuleProjectUpfront() throws Exception {
List<? extends SymbolInformation> allSymbols = indexer.getAllSymbols("");
assertEquals(6, allSymbols.size());
assertEquals(7, allSymbols.size());
String docUri = directory.toPath().resolve("src/main/java/org/test/MainClass.java").toUri().toString();
assertTrue(containsSymbol(allSymbols, "@+ 'mainClass' (@SpringBootApplication <: @SpringBootConfiguration, @Configuration, @Component) MainClass", docUri, 6, 0, 6, 22));
@@ -118,6 +121,10 @@ public class SpringIndexerTest {
docUri = directory.toPath().resolve("src/main/java/org/test/sub/MappingClassSubpackage.java").toUri().toString();
assertTrue(containsSymbol(allSymbols, "@/classlevel/mapping-subpackage", docUri, 7, 1, 7, 38));
docUri = directory.toPath().resolve("src/main/java/org/test/ClassWithDefaultSymbol.java").toUri().toString();
assertTrue(containsSymbol(allSymbols, "@Configurable", docUri, 4, 0, 4, 13));
}
@Test
@@ -139,7 +146,7 @@ public class SpringIndexerTest {
// check for updated index in all symbols
List<? extends SymbolInformation> allSymbols = indexer.getAllSymbols("");
assertEquals(6, allSymbols.size());
assertEquals(7, allSymbols.size());
String docUri = directory.toPath().resolve("src/main/java/org/test/MainClass.java").toUri().toString();
assertTrue(containsSymbol(allSymbols, "@+ 'mainClass' (@SpringBootApplication <: @SpringBootConfiguration, @Configuration, @Component) MainClass", docUri, 6, 0, 6, 22));
@@ -152,6 +159,9 @@ public class SpringIndexerTest {
docUri = directory.toPath().resolve("src/main/java/org/test/sub/MappingClassSubpackage.java").toUri().toString();
assertTrue(containsSymbol(allSymbols, "@/classlevel/mapping-subpackage", docUri, 7, 1, 7, 38));
docUri = directory.toPath().resolve("src/main/java/org/test/ClassWithDefaultSymbol.java").toUri().toString();
assertTrue(containsSymbol(allSymbols, "@Configurable", docUri, 4, 0, 4, 13));
}
@Test
@@ -163,7 +173,7 @@ public class SpringIndexerTest {
assertNull(symbols);
List<? extends SymbolInformation> allSymbols = indexer.getAllSymbols("");
assertEquals(6, allSymbols.size());
assertEquals(7, allSymbols.size());
try {
// create document and update index
@@ -197,7 +207,7 @@ public class SpringIndexerTest {
// check for updated index in all symbols
allSymbols = indexer.getAllSymbols("");
assertEquals(8, allSymbols.size());
assertEquals(9, allSymbols.size());
String docUri = directory.toPath().resolve("src/main/java/org/test/MainClass.java").toUri().toString();
assertTrue(containsSymbol(allSymbols, "@+ 'mainClass' (@SpringBootApplication <: @SpringBootConfiguration, @Configuration, @Component) MainClass", docUri, 6, 0, 6, 22));
@@ -211,6 +221,9 @@ public class SpringIndexerTest {
docUri = directory.toPath().resolve("src/main/java/org/test/sub/MappingClassSubpackage.java").toUri().toString();
assertTrue(containsSymbol(allSymbols, "@/classlevel/mapping-subpackage", docUri, 7, 1, 7, 38));
docUri = directory.toPath().resolve("src/main/java/org/test/ClassWithDefaultSymbol.java").toUri().toString();
assertTrue(containsSymbol(allSymbols, "@Configurable", docUri, 4, 0, 4, 13));
assertTrue(containsSymbol(allSymbols, "@/created-mapping1", createdDocURI, 6, 1, 6, 36));
assertTrue(containsSymbol(allSymbols, "@/created-mapping2", createdDocURI, 11, 1, 11, 36));
}
@@ -233,7 +246,7 @@ public class SpringIndexerTest {
// check for updated index in all symbols
List<? extends SymbolInformation> allSymbols = indexer.getAllSymbols("");
assertEquals(4, allSymbols.size());
assertEquals(5, allSymbols.size());
String docUri = directory.toPath().resolve("src/main/java/org/test/MainClass.java").toUri().toString();
assertTrue(containsSymbol(allSymbols, "@+ 'mainClass' (@SpringBootApplication <: @SpringBootConfiguration, @Configuration, @Component) MainClass", docUri, 6, 0, 6, 22));
@@ -242,6 +255,9 @@ public class SpringIndexerTest {
docUri = directory.toPath().resolve("src/main/java/org/test/sub/MappingClassSubpackage.java").toUri().toString();
assertTrue(containsSymbol(allSymbols, "@/classlevel/mapping-subpackage", docUri, 7, 1, 7, 38));
docUri = directory.toPath().resolve("src/main/java/org/test/ClassWithDefaultSymbol.java").toUri().toString();
assertTrue(containsSymbol(allSymbols, "@Configurable", docUri, 4, 0, 4, 13));
}
@Test
@@ -289,7 +305,7 @@ public class SpringIndexerTest {
SymbolInformation symbol = iterator.next();
if (
symbol.getName().equals(name) &&
symbol.getName().equals(name) &&
symbol.getLocation().getUri().equals(uri)
) {
return true;
@@ -315,11 +331,11 @@ public class SpringIndexerTest {
return false;
}
@Test
public void testDeleteProject() throws Exception {
List<? extends SymbolInformation> allSymbols = indexer.getAllSymbols("");
assertEquals(6, allSymbols.size());
assertEquals(7, allSymbols.size());
CompletableFuture<Void> deleteProject = indexer.deleteProject(project);
deleteProject.get(5, TimeUnit.SECONDS);

View File

@@ -0,0 +1,30 @@
package org.test;
import org.springframework.boot.autoconfigure.condition.ConditionalOnJava;
import org.springframework.boot.autoconfigure.condition.ConditionalOnJava.JavaVersion;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
@Configuration
public class ConfigurationWithConditionals {
@Bean
@ConditionalOnJava(JavaVersion.EIGHT)
public BeanClass conditionalBean() {
return new BeanClass();
}
@ConditionalOnJava(JavaVersion.EIGHT)
@Bean
public BeanClass conditionalBeanDifferentSequence() {
return new BeanClass();
}
@Bean
@ConditionalOnJava(JavaVersion.EIGHT)
@Profile("cloud")
public BeanClass conditionalBeanWithJavaAndCloud() {
return new BeanClass();
}
}

View File

@@ -0,0 +1,27 @@
package org.test;
import org.springframework.boot.autoconfigure.condition.ConditionalOnJava;
import org.springframework.boot.autoconfigure.condition.ConditionalOnJava.JavaVersion;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
@Configuration
public class ConfigurationWithConditionalsDefaultSymbols {
@ConditionalOnJava(JavaVersion.EIGHT)
public BeanClass conditionalBean() {
return new BeanClass();
}
@Profile("cloud")
public BeanClass conditionalBeanWithCloud() {
return new BeanClass();
}
@ConditionalOnJava(JavaVersion.EIGHT)
@Profile("cloud")
public BeanClass conditionalBeanWithJavaAndCloud() {
return new BeanClass();
}
}

View File

@@ -0,0 +1,7 @@
package org.test;
import org.springframework.beans.factory.annotation.Configurable;
@Configurable
public class ClassWithDefaultSymbol {
}