Skip to content

Commit b90a476

Browse files
committed
GH-1640: take source-defined stereotypes into account, when enabled
1 parent 377aa6a commit b90a476

File tree

10 files changed

+142
-154
lines changed

10 files changed

+142
-154
lines changed

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/SpringSymbolIndexerConfig.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,14 @@
2525
import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider;
2626
import org.springframework.ide.vscode.boot.java.requestmapping.HttpExchangeSymbolProvider;
2727
import org.springframework.ide.vscode.boot.java.requestmapping.RequestMappingSymbolProvider;
28-
import org.springframework.ide.vscode.boot.java.stereotypes.StereotypeCatalogRegistry;
2928
import org.springframework.ide.vscode.boot.java.stereotypes.StereotypesIndexer;
3029
import org.springframework.ide.vscode.boot.java.utils.RestrictedDefaultSymbolProvider;
3130

3231
@Configuration(proxyBeanMethods = false)
3332
public class SpringSymbolIndexerConfig {
3433

3534
@Bean
36-
AnnotationHierarchyAwareLookup<SymbolProvider> symbolProviders(IndexCache cache, DataRepositoryAotMetadataService repositoryMetadataService, StereotypeCatalogRegistry stereotypeCatalogRegistry) {
35+
AnnotationHierarchyAwareLookup<SymbolProvider> symbolProviders(IndexCache cache, DataRepositoryAotMetadataService repositoryMetadataService) {
3736
AnnotationHierarchyAwareLookup<SymbolProvider> providers = new AnnotationHierarchyAwareLookup<>();
3837

3938
RequestMappingSymbolProvider requestMappingSymbolProvider = new RequestMappingSymbolProvider();
@@ -85,7 +84,7 @@ AnnotationHierarchyAwareLookup<SymbolProvider> symbolProviders(IndexCache cache,
8584
providers.put(Annotations.FEIGN_CLIENT, new FeignClientSymbolProvider());
8685
providers.put(Annotations.HTTP_EXCHANGE, new HttpExchangeSymbolProvider());
8786

88-
providers.put(Annotations.JMOLECULES_STEREOTYPE, new StereotypesIndexer(stereotypeCatalogRegistry));
87+
providers.put(Annotations.JMOLECULES_STEREOTYPE, new StereotypesIndexer());
8988

9089
return providers;
9190
}

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/commands/JMoleculesStructureView.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
import java.util.List;
1414
import java.util.function.BiConsumer;
1515

16-
import org.atteo.evo.inflector.English;
1716
import org.jmolecules.stereotype.catalog.support.AbstractStereotypeCatalog;
1817
import org.jmolecules.stereotype.tooling.HierarchicalNodeHandler;
1918
import org.jmolecules.stereotype.tooling.ProjectTree;
@@ -36,11 +35,8 @@ public JMoleculesStructureView(AbstractStereotypeCatalog catalog, SpringMetamode
3635
this.springIndex = springIndex;
3736
}
3837

39-
public Node createTree(IJavaProject project) {
38+
public Node createTree(IJavaProject project, IndexBasedStereotypeFactory factory) {
4039

41-
var factory = new IndexBasedStereotypeFactory(catalog, springIndex);
42-
factory.registerStereotypeDefinitions();
43-
4440
StereotypePackageElement mainApplicationPackage = StructureViewUtil.identifyMainApplicationPackage(project, springIndex);
4541

4642
var labelProvider = new SimpleLabelProvider<>(StereotypePackageElement::getPackageName, StereotypePackageElement::getPackageName, StereotypeClassElement::getType,

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/commands/ModulithStructureView.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,8 @@ public ModulithStructureView(AbstractStereotypeCatalog catalog, SpringMetamodelI
3535
this.modulithService = modulithService;
3636
}
3737

38-
public Node createTree(IJavaProject project) {
39-
40-
var factory = new IndexBasedStereotypeFactory(catalog, springIndex);
41-
factory.registerStereotypeDefinitions();
38+
public Node createTree(IJavaProject project, IndexBasedStereotypeFactory factory) {
39+
4240
var adapter = new ModulithStereotypeFactoryAdapter(factory);
4341

4442
AppModules modulesData = modulithService.getModulesData(project);

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/commands/SpringIndexCommands.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import org.slf4j.LoggerFactory;
1818
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
1919
import org.springframework.ide.vscode.boot.java.commands.JsonNodeHandler.Node;
20+
import org.springframework.ide.vscode.boot.java.stereotypes.IndexBasedStereotypeFactory;
2021
import org.springframework.ide.vscode.boot.java.stereotypes.StereotypeCatalogRegistry;
2122
import org.springframework.ide.vscode.boot.modulith.ModulithService;
2223
import org.springframework.ide.vscode.commons.java.IJavaProject;
@@ -62,12 +63,17 @@ private Node nodeFrom(IJavaProject project, boolean updateMetadata) {
6263
}
6364

6465
var catalog = stereotypeCatalogRegistry.getCatalogOf(project);
66+
var factory = new IndexBasedStereotypeFactory(catalog, springIndex);
67+
68+
if (System.getProperty("enable-source-defined-stereotypes") != null) {
69+
factory.registerStereotypeDefinitions();
70+
}
6571

6672
if (ModulithService.isModulithDependentProject(project) && System.getProperty("disable-modulith-structure-view") == null) {
67-
return new ModulithStructureView(catalog, springIndex, modulithService).createTree(project);
73+
return new ModulithStructureView(catalog, springIndex, modulithService).createTree(project, factory);
6874
}
6975
else {
70-
return new JMoleculesStructureView(catalog, springIndex).createTree(project);
76+
return new JMoleculesStructureView(catalog, springIndex).createTree(project, factory);
7177
}
7278
}
7379

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/stereotypes/IndexBasedStereotypeFactory.java

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import org.jmolecules.stereotype.catalog.support.AbstractStereotypeCatalog;
2424
import org.jmolecules.stereotype.catalog.support.StereotypeDetector.AnalysisLevel;
2525
import org.jmolecules.stereotype.catalog.support.StereotypeMatcher;
26-
import org.jmolecules.stereotype.support.StringBasedStereotype;
2726
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
2827

2928
public class IndexBasedStereotypeFactory implements StereotypeFactory<StereotypePackageElement, StereotypeClassElement, StereotypeMethodElement> {
@@ -41,19 +40,6 @@ public IndexBasedStereotypeFactory(AbstractStereotypeCatalog catalog, SpringMeta
4140
this.springIndex = springIndex;
4241
}
4342

44-
public void registerStereotypeDefinitions() {
45-
springIndex.getNodesOfType(StereotypeDefinitionElement.class).stream()
46-
.forEach(element -> registerStereotype(element));
47-
}
48-
49-
private void registerStereotype(StereotypeDefinitionElement element) {
50-
var stereotype = StringBasedStereotype.of(element.getType());
51-
Type assignment = element.getAssignment();
52-
53-
catalog.getOrRegister(stereotype, () -> Assignment.of(element.getType(), assignment))
54-
.getStereotype();
55-
}
56-
5743
@Override
5844
public Stereotypes fromPackage(StereotypePackageElement pkg) {
5945
return new Stereotypes(fromAnnotatedElement(pkg, AnalysisLevel.DIRECT));
@@ -70,6 +56,11 @@ public Stereotypes fromMethod(StereotypeMethodElement method) {
7056
return new Stereotypes(fromAnnotatedElement(method, AnalysisLevel.DIRECT));
7157
}
7258

59+
public void registerStereotypeDefinitions() {
60+
springIndex.getNodesOfType(StereotypeDefinitionElement.class).stream()
61+
.forEach(element -> registerStereotype(element));
62+
}
63+
7364
private <T extends StereotypeAnnotatedElement> Collection<Stereotype> fromAnnotatedElement(StereotypeAnnotatedElement element, AnalysisLevel level) {
7465

7566
var result = new ArrayList<Stereotype>();
@@ -124,8 +115,6 @@ private Collection<Stereotype> fromTypeInternal(StereotypeClassElement type, Ana
124115

125116
return result;
126117
}
127-
128-
129118

130119
private static boolean isAnnotated(StereotypeAnnotatedElement element, String fqn) {
131120
return element.getAnnotationTypes().stream()
@@ -146,13 +135,15 @@ private StereotypePackageElement findPackageFor(StereotypeClassElement type) {
146135

147136
return result.isPresent() ? result.get() : null;
148137
}
138+
139+
private void registerStereotype(StereotypeDefinitionElement element) {
140+
var stereotype = element.createStereotype();
149141

150-
151-
152-
153-
154-
155-
156-
142+
String type = element.getType();
143+
Type assignment = element.getAssignment();
144+
145+
catalog.getOrRegister(stereotype, () -> Assignment.of(type, assignment))
146+
.getStereotype();
147+
}
157148

158149
}

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/stereotypes/StereotypeDefinitionElement.java

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,24 @@
1010
*******************************************************************************/
1111
package org.springframework.ide.vscode.boot.java.stereotypes;
1212

13+
import org.jmolecules.stereotype.api.Stereotype;
1314
import org.jmolecules.stereotype.catalog.StereotypeDefinition.Assignment.Type;
15+
import org.jmolecules.stereotype.support.StringBasedStereotype;
1416
import org.springframework.ide.vscode.commons.protocol.spring.AbstractSpringIndexElement;
1517

1618
public class StereotypeDefinitionElement extends AbstractSpringIndexElement {
1719

1820
private final String type;
21+
private final int priority;
22+
private final String displayName;
23+
private final String[] groups;
1924
private final Type assignment;
2025

21-
public StereotypeDefinitionElement(String type, Type assignment) {
26+
public StereotypeDefinitionElement(String type, int priority, String displayName, String[] groups, Type assignment) {
2227
this.type = type;
28+
this.priority = priority;
29+
this.displayName = displayName;
30+
this.groups = groups;
2331
this.assignment = assignment;
2432
}
2533

@@ -30,4 +38,21 @@ public String getType() {
3038
public Type getAssignment() {
3139
return this.assignment;
3240
}
41+
42+
public Stereotype createStereotype() {
43+
var stereotype = StringBasedStereotype.of(type, priority);
44+
45+
// add assigment
46+
stereotype = stereotype.withDisplayName(displayName);
47+
48+
// add groups
49+
if (groups != null) {
50+
for (String group : groups) {
51+
stereotype = stereotype.addGroup(group);
52+
}
53+
}
54+
55+
return stereotype;
56+
}
57+
3358
}

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/stereotypes/StereotypesIndexer.java

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@
1515
import java.util.Collection;
1616
import java.util.Iterator;
1717
import java.util.List;
18+
import java.util.Optional;
1819
import java.util.Set;
1920
import java.util.stream.Stream;
2021

2122
import org.eclipse.jdt.core.dom.ASTNode;
2223
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
2324
import org.eclipse.jdt.core.dom.Annotation;
2425
import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration;
26+
import org.eclipse.jdt.core.dom.Expression;
2527
import org.eclipse.jdt.core.dom.IAnnotationBinding;
2628
import org.eclipse.jdt.core.dom.IPackageBinding;
2729
import org.eclipse.jdt.core.dom.ITypeBinding;
@@ -32,10 +34,11 @@
3234
import org.eclipse.jdt.core.dom.SimpleName;
3335
import org.eclipse.jdt.core.dom.TypeDeclaration;
3436
import org.eclipse.lsp4j.Location;
37+
import org.jmolecules.stereotype.api.Stereotype;
38+
import org.jmolecules.stereotype.catalog.StereotypeDefinition;
3539
import org.jmolecules.stereotype.catalog.StereotypeDefinition.Assignment.Type;
3640
import org.slf4j.Logger;
3741
import org.slf4j.LoggerFactory;
38-
import org.springframework.ide.vscode.boot.java.Annotations;
3942
import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchies;
4043
import org.springframework.ide.vscode.boot.java.beans.CachedBean;
4144
import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider;
@@ -53,12 +56,6 @@ public class StereotypesIndexer implements SymbolProvider {
5356

5457
private static final Logger log = LoggerFactory.getLogger(StereotypesIndexer.class);
5558

56-
private final StereotypeCatalogRegistry stereotypeCatalogRegistry;
57-
58-
public StereotypesIndexer(StereotypeCatalogRegistry stereotypeCatalogRegistry) {
59-
this.stereotypeCatalogRegistry = stereotypeCatalogRegistry;
60-
}
61-
6259
@Override
6360
public void addSymbols(Annotation node, ITypeBinding typeBinding, Collection<ITypeBinding> metaAnnotations, SpringIndexerJavaContext context, TextDocument doc) {
6461
ASTNode parent = node.getParent();
@@ -67,7 +64,13 @@ public void addSymbols(Annotation node, ITypeBinding typeBinding, Collection<ITy
6764
AnnotationTypeDeclaration annotationType = (AnnotationTypeDeclaration) parent;
6865
ITypeBinding annotationBinding = annotationType.resolveBinding();
6966

70-
StereotypeDefinitionElement stereotypeDefinitionElement = new StereotypeDefinitionElement(annotationBinding.getQualifiedName(), Type.IS_ANNOTATED);
67+
StereotypeDefinitionElement stereotypeDefinitionElement = createDefinitionElement(annotationBinding.getQualifiedName(), Type.IS_ANNOTATED, node, doc);
68+
context.getBeans().add(new CachedBean(context.getDocURI(), stereotypeDefinitionElement));
69+
}
70+
else if (parent instanceof TypeDeclaration && ((TypeDeclaration) parent).isInterface()) {
71+
ITypeBinding interfaceType = ((TypeDeclaration) parent).resolveBinding();
72+
73+
StereotypeDefinitionElement stereotypeDefinitionElement = createDefinitionElement(interfaceType.getQualifiedName(), Type.IMPLEMENTS, node, doc);
7174
context.getBeans().add(new CachedBean(context.getDocURI(), stereotypeDefinitionElement));
7275
}
7376
}
@@ -136,18 +139,6 @@ private void createStereotypeElementForType(AbstractTypeDeclaration typeDeclarat
136139
return;
137140
}
138141

139-
// identify stereotype definitions themselves
140-
if (typeDeclaration instanceof TypeDeclaration && ((TypeDeclaration) typeDeclaration).isInterface()) {
141-
Collection<Annotation> annotations = ASTUtils.getAnnotations(typeDeclaration);
142-
boolean isStereotypeAnnotated = annotations.stream()
143-
.anyMatch(annotation -> annotation.resolveTypeBinding().getQualifiedName().equals(Annotations.JMOLECULES_STEREOTYPE));
144-
145-
if (isStereotypeAnnotated) {
146-
StereotypeDefinitionElement stereotypeDefinitionElement = new StereotypeDefinitionElement(typeBinding.getQualifiedName(), Type.IS_ANNOTATED);
147-
context.getBeans().add(new CachedBean(context.getDocURI(), stereotypeDefinitionElement));
148-
}
149-
}
150-
151142
// capture type information element for later tree generation
152143
AnnotationHierarchies annotationHierarchies = AnnotationHierarchies.get(typeDeclaration);
153144

@@ -233,13 +224,27 @@ private Collection<IAnnotationBinding> getMetaAnnotations(AnnotationHierarchies
233224
return result;
234225
}
235226

236-
private boolean isStereotype(TypeDeclaration typeDeclaration, ITypeBinding binding) {
237-
AnnotationHierarchies annotationHierarchies = AnnotationHierarchies.get(typeDeclaration);
238-
boolean isStereotype = annotationHierarchies.isAnnotatedWith(binding, Annotations.JMOLECULES_STEREOTYPE);
239-
240-
return isStereotype;
227+
private StereotypeDefinitionElement createDefinitionElement(String id, StereotypeDefinition.Assignment.Type assignment, Annotation additionalDetails, TextDocument doc) {
228+
int priority = Stereotype.DEFAULT_PRIORITY;
229+
Optional<Expression> attribute = ASTUtils.getAttribute(additionalDetails, "priority");
230+
if (attribute.isPresent()) {
231+
String priorityValue = ASTUtils.getExpressionValueAsString(attribute.get(), (a) -> {});
232+
priority = Integer.valueOf(priorityValue);
233+
}
234+
235+
String displayName = null;
236+
Optional<Expression> name = ASTUtils.getAttribute(additionalDetails, "name");
237+
if (name.isPresent()) {
238+
displayName = ASTUtils.getExpressionValueAsString(name.get(), (a) -> {});
239+
}
240+
241+
String[] groups = null;
242+
Optional<Expression> groupsAttribute = ASTUtils.getAttribute(additionalDetails, "groups");
243+
if (groupsAttribute.isPresent()) {
244+
groups = ASTUtils.getExpressionValueAsArray(groupsAttribute.get(), (a) -> {});
245+
}
246+
247+
return new StereotypeDefinitionElement(id, priority, displayName, groups, assignment);
241248
}
242249

243-
244-
245250
}

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexerJava.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ public class SpringIndexerJava implements SpringIndexer {
9494

9595
// whenever the implementation of the indexer changes in a way that the stored data in the cache is no longer valid,
9696
// we need to change the generation - this will result in a re-indexing due to no up-to-date cache data being found
97-
private static final String GENERATION = "GEN-28";
97+
private static final String GENERATION = "GEN-29";
9898
private static final String INDEX_FILES_TASK_ID = "index-java-source-files-task-";
9999

100100
private static final String SYMBOL_KEY = "symbols";

0 commit comments

Comments
 (0)