GH-1261: initial steps to include details about used annotations at injection points
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2024 Broadcom
|
||||
* 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
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Broadcom - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.commons.protocol.spring;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class AnnotationMetadata {
|
||||
|
||||
private final String annotationType;
|
||||
private final boolean isMetaAnnotation;
|
||||
private final Map<String, String[]> attributes;
|
||||
|
||||
public AnnotationMetadata(String annotationType, boolean isMetaAnnotation, Map<String, String[]> attributes) {
|
||||
this.annotationType = annotationType;
|
||||
this.isMetaAnnotation = isMetaAnnotation;
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
public String getAnnotationType() {
|
||||
return annotationType;
|
||||
}
|
||||
|
||||
public boolean isMetaAnnotation() {
|
||||
return isMetaAnnotation;
|
||||
}
|
||||
|
||||
public Map<String, String[]> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,14 +23,13 @@ public class Bean {
|
||||
private final Location location;
|
||||
private final InjectionPoint[] injectionPoints;
|
||||
private final Set<String> supertypes;
|
||||
private final String[] annotations;
|
||||
private final AnnotationMetadata[] annotations;
|
||||
|
||||
public Bean(String name, String type, Location location, InjectionPoint[] injectionPoints, Set<String> supertypes, String[] annotations) {
|
||||
public Bean(String name, String type, Location location, InjectionPoint[] injectionPoints, Set<String> supertypes, AnnotationMetadata[] annotations) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.location = location;
|
||||
this.annotations = annotations;
|
||||
|
||||
|
||||
if (injectionPoints != null && injectionPoints.length == 0) {
|
||||
this.injectionPoints = DefaultValues.EMPTY_INJECTION_POINTS;
|
||||
}
|
||||
@@ -48,6 +47,12 @@ public class Bean {
|
||||
this.supertypes = supertypes;
|
||||
}
|
||||
|
||||
if (annotations != null && annotations.length == 0) {
|
||||
this.annotations = DefaultValues.EMPTY_ANNOTATIONS;
|
||||
}
|
||||
else {
|
||||
this.annotations = annotations;
|
||||
}
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
@@ -70,7 +75,7 @@ public class Bean {
|
||||
return type != null && ((this.type != null && this.type.equals(type)) || (supertypes.contains(type)));
|
||||
}
|
||||
|
||||
public String[] getAnnotations() {
|
||||
public AnnotationMetadata[] getAnnotations() {
|
||||
return annotations;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ public class DefaultValues {
|
||||
|
||||
public static final Set<String> EMPTY_SUPERTYPES = new HashSet<>();
|
||||
public static final Set<String> OBJECT_SUPERTYPE = Set.of("java.lang.Object");
|
||||
|
||||
public static final AnnotationMetadata[] EMPTY_ANNOTATIONS = new AnnotationMetadata[0];
|
||||
public static final InjectionPoint[] EMPTY_INJECTION_POINTS = new InjectionPoint[0];
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2023 VMware, Inc.
|
||||
* Copyright (c) 2023, 2024 VMware, 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
|
||||
@@ -12,17 +12,29 @@ package org.springframework.ide.vscode.commons.protocol.spring;
|
||||
|
||||
import org.eclipse.lsp4j.Location;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class InjectionPoint {
|
||||
|
||||
private final String name;
|
||||
private final String type;
|
||||
private final Location location;
|
||||
private final AnnotationMetadata[] annotations;
|
||||
|
||||
public InjectionPoint(String name, String type, Location location) {
|
||||
public InjectionPoint(String name, String type, Location location, AnnotationMetadata[] annotations) {
|
||||
super();
|
||||
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.location = location;
|
||||
|
||||
if (annotations == null || (annotations != null && annotations.length == 0)) {
|
||||
this.annotations = DefaultValues.EMPTY_ANNOTATIONS;
|
||||
}
|
||||
else {
|
||||
this.annotations = annotations;
|
||||
}
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
@@ -36,5 +48,9 @@ public class InjectionPoint {
|
||||
public Location getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
public AnnotationMetadata[] getAnnotations() {
|
||||
return annotations;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
|
||||
import org.springframework.ide.vscode.boot.java.Annotations;
|
||||
import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchies;
|
||||
import org.springframework.ide.vscode.boot.java.beans.DependsOnCompletionProcessor;
|
||||
import org.springframework.ide.vscode.boot.java.beans.QualifierCompletionProcessor;
|
||||
import org.springframework.ide.vscode.boot.java.data.DataRepositoryCompletionProcessor;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.BootJavaCompletionEngine;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.CompletionProvider;
|
||||
@@ -113,6 +114,7 @@ public class BootJavaCompletionEngineConfigurer {
|
||||
providers.put(Annotations.SCOPE, new ScopeCompletionProcessor());
|
||||
providers.put(Annotations.VALUE, new ValueCompletionProcessor(javaProjectFinder, indexProvider, adHocProperties));
|
||||
providers.put(Annotations.DEPENDS_ON, new DependsOnCompletionProcessor(javaProjectFinder, springIndex));
|
||||
providers.put(Annotations.QUALIFIER, new QualifierCompletionProcessor(javaProjectFinder, springIndex));
|
||||
providers.put(Annotations.REPOSITORY, new DataRepositoryCompletionProcessor());
|
||||
|
||||
return new BootJavaCompletionEngine(cuCache, providers, snippetManager);
|
||||
|
||||
@@ -46,6 +46,7 @@ import org.springframework.ide.vscode.boot.index.cache.IndexCacheOnDisc;
|
||||
import org.springframework.ide.vscode.boot.index.cache.IndexCacheVoid;
|
||||
import org.springframework.ide.vscode.boot.java.JavaDefinitionHandler;
|
||||
import org.springframework.ide.vscode.boot.java.beans.DependsOnDefinitionProvider;
|
||||
import org.springframework.ide.vscode.boot.java.beans.QualifierDefinitionProvider;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.BootJavaCodeActionProvider;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.BootJavaReconcileEngine;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.JavaCodeActionHandler;
|
||||
@@ -393,7 +394,10 @@ public class BootLanguageServerBootApp {
|
||||
|
||||
@Bean
|
||||
JavaDefinitionHandler javaDefinitionHandler(CompilationUnitCache cuCache, JavaProjectFinder projectFinder, SpringMetamodelIndex springIndex) {
|
||||
return new JavaDefinitionHandler(cuCache, projectFinder, List.of(new PropertyValueAnnotationDefProvider(), new DependsOnDefinitionProvider(springIndex)));
|
||||
return new JavaDefinitionHandler(cuCache, projectFinder, List.of(
|
||||
new PropertyValueAnnotationDefProvider(),
|
||||
new DependsOnDefinitionProvider(springIndex),
|
||||
new QualifierDefinitionProvider(springIndex)));
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -35,7 +35,9 @@ import org.eclipse.lsp4j.Location;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.SymbolAddOnInformation;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.AnnotationMetadata;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.DefaultValues;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.InjectionPoint;
|
||||
import org.springframework.ide.vscode.commons.util.UriUtil;
|
||||
|
||||
@@ -351,6 +353,7 @@ public class IndexCacheOnDisc implements IndexCache {
|
||||
return new GsonBuilder()
|
||||
.registerTypeAdapter(SymbolAddOnInformation.class, new SymbolAddOnInformationAdapter())
|
||||
.registerTypeAdapter(Bean.class, new BeanJsonAdapter())
|
||||
.registerTypeAdapter(InjectionPoint.class, new InjectionPointJsonAdapter())
|
||||
.registerTypeAdapter(IndexCacheStore.class, new IndexCacheStoreAdapter())
|
||||
.create();
|
||||
}
|
||||
@@ -472,10 +475,29 @@ public class IndexCacheOnDisc implements IndexCache {
|
||||
Set<String> supertypes = context.deserialize(supertypesObject, Set.class);
|
||||
|
||||
JsonElement annotationsObject = parsedObject.get("annotations");
|
||||
String[] annotations = annotationsObject == null? new String[0] : context.deserialize(annotationsObject, String[].class);
|
||||
AnnotationMetadata[] annotations = annotationsObject == null ? DefaultValues.EMPTY_ANNOTATIONS : context.deserialize(annotationsObject, AnnotationMetadata[].class);
|
||||
|
||||
return new Bean(beanName, beanType, location, injectionPoints, supertypes, annotations);
|
||||
}
|
||||
}
|
||||
|
||||
private static class InjectionPointJsonAdapter implements JsonDeserializer<InjectionPoint> {
|
||||
|
||||
@Override
|
||||
public InjectionPoint deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
|
||||
JsonObject parsedObject = json.getAsJsonObject();
|
||||
|
||||
String injectionPointName = parsedObject.get("name").getAsString();
|
||||
String injectionPointType = parsedObject.get("type").getAsString();
|
||||
|
||||
JsonElement locationObject = parsedObject.get("location");
|
||||
Location location = context.deserialize(locationObject, Location.class);
|
||||
|
||||
JsonElement annotationsObject = parsedObject.get("annotations");
|
||||
AnnotationMetadata[] annotations = annotationsObject == null ? DefaultValues.EMPTY_ANNOTATIONS : context.deserialize(annotationsObject, AnnotationMetadata[].class);
|
||||
|
||||
return new InjectionPoint(injectionPointName, injectionPointType, location, annotations);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ import org.eclipse.lsp4j.jsonrpc.messages.Either;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ide.vscode.boot.java.Annotations;
|
||||
import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchies;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.AbstractSymbolProvider;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.EnhancedSymbolInformation;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.SymbolAddOnInformation;
|
||||
@@ -40,6 +39,7 @@ import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
|
||||
import org.springframework.ide.vscode.boot.java.utils.CachedSymbol;
|
||||
import org.springframework.ide.vscode.boot.java.utils.FunctionUtils;
|
||||
import org.springframework.ide.vscode.boot.java.utils.SpringIndexerJavaContext;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.AnnotationMetadata;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.InjectionPoint;
|
||||
import org.springframework.ide.vscode.commons.util.BadLocationException;
|
||||
@@ -95,9 +95,16 @@ public class BeansSymbolProvider extends AbstractSymbolProvider {
|
||||
Set<String> supertypes = new HashSet<>();
|
||||
ASTUtils.findSupertypes(beanType, supertypes);
|
||||
|
||||
String[] annotations = AnnotationHierarchies
|
||||
.findTransitiveSuperAnnotationBindings(node.resolveAnnotationBinding())
|
||||
.map(t -> t.getAnnotationType().getQualifiedName()).toArray(String[]::new);
|
||||
Collection<Annotation> annotationsOnMethod = ASTUtils.getAnnotations(method);
|
||||
AnnotationMetadata[] annotations = annotationsOnMethod.stream()
|
||||
.map(an -> an.resolveAnnotationBinding())
|
||||
.map(t -> new AnnotationMetadata(t.getAnnotationType().getQualifiedName(), false, ASTUtils.getAttributes(t)))
|
||||
.toArray(AnnotationMetadata[]::new);
|
||||
|
||||
// AnnotationMetadata[] annotations = AnnotationHierarchies
|
||||
// .findTransitiveSuperAnnotationBindings(node.resolveAnnotationBinding())
|
||||
// .map(t -> new AnnotationMetadata(t.getAnnotationType().getQualifiedName(), false, getAttributes(t)))
|
||||
// .toArray(AnnotationMetadata[]::new);
|
||||
|
||||
Bean beanDefinition = new Bean(nameAndRegion.getT1(), beanType.getQualifiedName(), location, injectionPoints, supertypes, annotations);
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ import org.springframework.ide.vscode.boot.java.handlers.SymbolAddOnInformation;
|
||||
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
|
||||
import org.springframework.ide.vscode.boot.java.utils.CachedSymbol;
|
||||
import org.springframework.ide.vscode.boot.java.utils.SpringIndexerJavaContext;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.AnnotationMetadata;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.InjectionPoint;
|
||||
import org.springframework.ide.vscode.commons.util.BadLocationException;
|
||||
@@ -66,6 +67,7 @@ public class ComponentSymbolProvider extends AbstractSymbolProvider {
|
||||
|
||||
protected Tuple.Two<EnhancedSymbolInformation, Bean> createSymbol(Annotation node, ITypeBinding annotationType, Collection<ITypeBinding> metaAnnotations, TextDocument doc) throws BadLocationException {
|
||||
String annotationTypeName = annotationType.getName();
|
||||
|
||||
Collection<String> metaAnnotationNames = metaAnnotations.stream()
|
||||
.map(ITypeBinding::getName)
|
||||
.collect(Collectors.toList());
|
||||
@@ -94,8 +96,17 @@ public class ComponentSymbolProvider extends AbstractSymbolProvider {
|
||||
Set<String> supertypes = new HashSet<>();
|
||||
ASTUtils.findSupertypes(beanType, supertypes);
|
||||
|
||||
String[] annotations = Stream.concat(Stream.of(annotationType), metaAnnotations.stream()).map(t -> t.getQualifiedName()).toArray(String[]::new);
|
||||
|
||||
Collection<Annotation> annotationsOnType = ASTUtils.getAnnotations(type);
|
||||
|
||||
AnnotationMetadata[] annotations = Stream.concat(
|
||||
annotationsOnType.stream()
|
||||
.map(an -> an.resolveAnnotationBinding())
|
||||
.map(t -> new AnnotationMetadata(t.getAnnotationType().getQualifiedName(), false, ASTUtils.getAttributes(t)))
|
||||
,
|
||||
metaAnnotations.stream()
|
||||
.map(an -> new AnnotationMetadata(an.getQualifiedName(), true, null)))
|
||||
.toArray(AnnotationMetadata[]::new);
|
||||
|
||||
Bean beanDefinition = new Bean(beanName, beanType.getQualifiedName(), location, injectionPoints, supertypes, annotations);
|
||||
|
||||
return Tuple.two(new EnhancedSymbolInformation(symbol, addon), beanDefinition);
|
||||
|
||||
@@ -40,6 +40,7 @@ import org.springframework.ide.vscode.boot.java.handlers.SymbolAddOnInformation;
|
||||
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
|
||||
import org.springframework.ide.vscode.boot.java.utils.CachedSymbol;
|
||||
import org.springframework.ide.vscode.boot.java.utils.SpringIndexerJavaContext;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.AnnotationMetadata;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.InjectionPoint;
|
||||
import org.springframework.ide.vscode.commons.util.BadLocationException;
|
||||
@@ -67,8 +68,7 @@ public class FeignClientSymbolProvider extends AbstractSymbolProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private Two<EnhancedSymbolInformation, Bean> createSymbol(Annotation node, ITypeBinding annotationType,
|
||||
Collection<ITypeBinding> metaAnnotations, TextDocument doc) throws BadLocationException {
|
||||
private Two<EnhancedSymbolInformation, Bean> createSymbol(Annotation node, ITypeBinding annotationType, Collection<ITypeBinding> metaAnnotations, TextDocument doc) throws BadLocationException {
|
||||
String annotationTypeName = annotationType.getName();
|
||||
Collection<String> metaAnnotationNames = metaAnnotations.stream()
|
||||
.map(ITypeBinding::getName)
|
||||
@@ -92,8 +92,17 @@ public class FeignClientSymbolProvider extends AbstractSymbolProvider {
|
||||
Set<String> supertypes = new HashSet<>();
|
||||
ASTUtils.findSupertypes(beanType, supertypes);
|
||||
|
||||
String[] annotations = Stream.concat(Stream.of(annotationType), metaAnnotations.stream()).map(t -> t.getQualifiedName()).toArray(String[]::new);
|
||||
|
||||
Collection<Annotation> annotationsOnType = ASTUtils.getAnnotations(type);
|
||||
|
||||
AnnotationMetadata[] annotations = Stream.concat(
|
||||
annotationsOnType.stream()
|
||||
.map(an -> an.resolveAnnotationBinding())
|
||||
.map(t -> new AnnotationMetadata(t.getAnnotationType().getQualifiedName(), false, ASTUtils.getAttributes(t)))
|
||||
,
|
||||
metaAnnotations.stream()
|
||||
.map(an -> new AnnotationMetadata(an.getQualifiedName(), true, null)))
|
||||
.toArray(AnnotationMetadata[]::new);
|
||||
|
||||
Bean beanDefinition = new Bean(beanName, beanType == null ? "" : beanType.getQualifiedName(), location, injectionPoints, supertypes, annotations);
|
||||
|
||||
return Tuple.two(new EnhancedSymbolInformation(symbol, addon), beanDefinition);
|
||||
|
||||
@@ -0,0 +1,275 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2024 Broadcom
|
||||
* 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
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Broadcom - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.beans;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
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.ArrayInitializer;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.MemberValuePair;
|
||||
import org.eclipse.jdt.core.dom.SimpleName;
|
||||
import org.eclipse.jdt.core.dom.StringLiteral;
|
||||
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.CompletionProvider;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
|
||||
import org.springframework.ide.vscode.commons.util.BadLocationException;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class QualifierCompletionProcessor implements CompletionProvider {
|
||||
|
||||
private final JavaProjectFinder projectFinder;
|
||||
private final SpringMetamodelIndex springIndex;
|
||||
|
||||
public QualifierCompletionProcessor(JavaProjectFinder projectFinder, SpringMetamodelIndex springIndex) {
|
||||
this.projectFinder = projectFinder;
|
||||
this.springIndex = springIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void provideCompletions(ASTNode node, Annotation annotation, ITypeBinding type, int offset, TextDocument doc, Collection<ICompletionProposal> completions) {
|
||||
|
||||
Optional<IJavaProject> optionalProject = projectFinder.find(doc.getId());
|
||||
if (!optionalProject.isPresent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
IJavaProject project = optionalProject.get();
|
||||
|
||||
try {
|
||||
|
||||
// case: @Qualifier(<*>)
|
||||
if (node == annotation && doc.get(offset - 1, 2).endsWith("()")) {
|
||||
Bean[] beans = this.springIndex.getBeansOfProject(project.getElementName());
|
||||
|
||||
for (Bean bean : beans) {
|
||||
|
||||
DocumentEdits edits = new DocumentEdits(doc, false);
|
||||
edits.replace(offset, offset, "\"" + bean.getName() + "\"");
|
||||
|
||||
// PT-160455522: create a proposal with `PlainText` format type, because for vscode (but not Eclipse), if you send it as a snippet
|
||||
// and it is "place holder" as such `"${debug}"`, vscode may treat it as a snippet place holder, and insert an empty string
|
||||
// if it cannot resolve it. If sending this as plain text, then insertion happens correctly
|
||||
QualifierCompletionProposal proposal = new QualifierCompletionProposal(edits, bean.getName(), bean.getName(), null);
|
||||
|
||||
completions.add(proposal);
|
||||
}
|
||||
}
|
||||
// case: @Qualifier(prefix<*>)
|
||||
else if (node instanceof SimpleName && node.getParent() instanceof Annotation) {
|
||||
computeProposalsForSimpleName(project, node, completions, offset, doc);
|
||||
}
|
||||
// case: @Qualifier(value=<*>)
|
||||
else if (node instanceof SimpleName && node.getParent() instanceof MemberValuePair
|
||||
&& "value".equals(((MemberValuePair)node.getParent()).getName().toString())) {
|
||||
computeProposalsForSimpleName(project, node, completions, offset, doc);
|
||||
}
|
||||
// case: @Qualifier("prefix<*>")
|
||||
else if (node instanceof StringLiteral && node.getParent() instanceof Annotation) {
|
||||
if (node.toString().startsWith("\"") && node.toString().endsWith("\"")) {
|
||||
computeProposalsForStringLiteral(project, node, completions, offset, doc);
|
||||
}
|
||||
}
|
||||
else if (node instanceof StringLiteral && node.getParent() instanceof ArrayInitializer) {
|
||||
if (node.toString().startsWith("\"") && node.toString().endsWith("\"")) {
|
||||
computeProposalsForInsideArrayInitializer(project, node, completions, offset, doc);
|
||||
}
|
||||
}
|
||||
// case: @Qualifier(value="prefix<*>")
|
||||
else if (node instanceof StringLiteral && node.getParent() instanceof MemberValuePair
|
||||
&& "value".equals(((MemberValuePair)node.getParent()).getName().toString())) {
|
||||
if (node.toString().startsWith("\"") && node.toString().endsWith("\"")) {
|
||||
computeProposalsForStringLiteral(project, node, completions, offset, doc);
|
||||
}
|
||||
}
|
||||
// case: @Qualifier({<*>})
|
||||
else if (node instanceof ArrayInitializer && node.getParent() instanceof Annotation) {
|
||||
computeProposalsForArrayInitializr(project, (ArrayInitializer) node, completions, offset, doc);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void computeProposalsForSimpleName(IJavaProject project, ASTNode node, Collection<ICompletionProposal> completions, int offset, IDocument doc) {
|
||||
String prefix = identifyPropertyPrefix(node.toString(), offset - node.getStartPosition());
|
||||
|
||||
int startOffset = node.getStartPosition();
|
||||
int endOffset = node.getStartPosition() + node.getLength();
|
||||
|
||||
String proposalPrefix = "\"";
|
||||
String proposalPostfix = "\"";
|
||||
|
||||
Set<String> mentionedBeans = alreadyMentionedBeans(node);
|
||||
|
||||
Bean[] beans = this.springIndex.getBeansOfProject(project.getElementName());
|
||||
List<Bean> matchingBeans = Arrays.stream(beans)
|
||||
.filter(bean -> bean.getName().toLowerCase().startsWith(prefix.toLowerCase()))
|
||||
.filter(bean -> !mentionedBeans.contains(bean.getName()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (Bean bean : matchingBeans) {
|
||||
|
||||
DocumentEdits edits = new DocumentEdits(doc, false);
|
||||
edits.replace(startOffset, endOffset, proposalPrefix + bean.getName() + proposalPostfix);
|
||||
|
||||
// PT-160455522: create a proposal with `PlainText` format type, because for vscode (but not Eclipse), if you send it as a snippet
|
||||
// and it is "place holder" as such `"${debug}"`, vscode may treat it as a snippet place holder, and insert an empty string
|
||||
// if it cannot resolve it. If sending this as plain text, then insertion happens correctly
|
||||
QualifierCompletionProposal proposal = new QualifierCompletionProposal(edits, bean.getName(), bean.getName(), null);
|
||||
|
||||
completions.add(proposal);
|
||||
}
|
||||
}
|
||||
|
||||
private void computeProposalsForStringLiteral(IJavaProject project, ASTNode node, Collection<ICompletionProposal> completions, int offset, IDocument doc) throws BadLocationException {
|
||||
int length = offset - (node.getStartPosition() + 1);
|
||||
|
||||
String prefix = identifyPropertyPrefix(doc.get(node.getStartPosition() + 1, length), length);
|
||||
int startOffset = offset - prefix.length();
|
||||
int endOffset = offset;
|
||||
|
||||
Set<String> mentionedBeans = alreadyMentionedBeans(node);
|
||||
|
||||
Bean[] beans = this.springIndex.getBeansOfProject(project.getElementName());
|
||||
|
||||
final String filterPrefix = prefix;
|
||||
List<Bean> matchingBeans = Arrays.stream(beans)
|
||||
.filter(bean -> bean.getName().toLowerCase().startsWith(filterPrefix.toLowerCase()))
|
||||
.filter(bean -> !mentionedBeans.contains(bean.getName()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (Bean bean : matchingBeans) {
|
||||
|
||||
DocumentEdits edits = new DocumentEdits(doc, false);
|
||||
edits.replace(startOffset, endOffset, bean.getName());
|
||||
|
||||
// PT-160455522: create a proposal with `PlainText` format type, because for vscode (but not Eclipse), if you send it as a snippet
|
||||
// and it is "place holder" as such `"${debug}"`, vscode may treat it as a snippet place holder, and insert an empty string
|
||||
// if it cannot resolve it. If sending this as plain text, then insertion happens correctly
|
||||
QualifierCompletionProposal proposal = new QualifierCompletionProposal(edits, bean.getName(), bean.getName(), null);
|
||||
|
||||
completions.add(proposal);
|
||||
}
|
||||
}
|
||||
|
||||
private void computeProposalsForArrayInitializr(IJavaProject project, ArrayInitializer node, Collection<ICompletionProposal> completions, int offset, IDocument doc) {
|
||||
Set<String> mentionedBeans = alreadyMentionedBeans(node);
|
||||
|
||||
Bean[] beans = this.springIndex.getBeansOfProject(project.getElementName());
|
||||
List<Bean> filteredBeans = Arrays.stream(beans)
|
||||
.filter(bean -> !mentionedBeans.contains(bean.getName()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (Bean bean : filteredBeans) {
|
||||
|
||||
DocumentEdits edits = new DocumentEdits(doc, false);
|
||||
edits.replace(offset, offset, "\"" + bean.getName() + "\"");
|
||||
|
||||
// PT-160455522: create a proposal with `PlainText` format type, because for vscode (but not Eclipse), if you send it as a snippet
|
||||
// and it is "place holder" as such `"${debug}"`, vscode may treat it as a snippet place holder, and insert an empty string
|
||||
// if it cannot resolve it. If sending this as plain text, then insertion happens correctly
|
||||
QualifierCompletionProposal proposal = new QualifierCompletionProposal(edits, bean.getName(), bean.getName(), null);
|
||||
|
||||
completions.add(proposal);
|
||||
}
|
||||
}
|
||||
|
||||
private void computeProposalsForInsideArrayInitializer(IJavaProject project, ASTNode node, Collection<ICompletionProposal> completions, int offset, TextDocument doc) throws BadLocationException {
|
||||
int length = offset - (node.getStartPosition() + 1);
|
||||
if (length >= 0) {
|
||||
computeProposalsForStringLiteral(project, node, completions, offset, doc);
|
||||
}
|
||||
else {
|
||||
Set<String> mentionedBeans = alreadyMentionedBeans(node);
|
||||
|
||||
Bean[] beans = this.springIndex.getBeansOfProject(project.getElementName());
|
||||
List<Bean> filteredBeans = Arrays.stream(beans)
|
||||
.filter(bean -> !mentionedBeans.contains(bean.getName()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (Bean bean : filteredBeans) {
|
||||
|
||||
DocumentEdits edits = new DocumentEdits(doc, false);
|
||||
edits.replace(offset, offset, "\"" + bean.getName() + "\",");
|
||||
|
||||
// PT-160455522: create a proposal with `PlainText` format type, because for vscode (but not Eclipse), if you send it as a snippet
|
||||
// and it is "place holder" as such `"${debug}"`, vscode may treat it as a snippet place holder, and insert an empty string
|
||||
// if it cannot resolve it. If sending this as plain text, then insertion happens correctly
|
||||
QualifierCompletionProposal proposal = new QualifierCompletionProposal(edits, bean.getName(), bean.getName(), null);
|
||||
|
||||
completions.add(proposal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String identifyPropertyPrefix(String nodeContent, int offset) {
|
||||
String result = nodeContent.substring(0, offset);
|
||||
|
||||
int i = offset - 1;
|
||||
while (i >= 0) {
|
||||
char c = nodeContent.charAt(i);
|
||||
if (c == '}' || c == '{' || c == '$' || c == '#') {
|
||||
result = result.substring(i + 1, offset);
|
||||
break;
|
||||
}
|
||||
i--;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Set<String> alreadyMentionedBeans(ASTNode node) {
|
||||
Set<String> result = new HashSet<>();
|
||||
|
||||
ArrayInitializer arrayNode = null;
|
||||
while (node != null && arrayNode == null && !(node instanceof Annotation)) {
|
||||
if (node instanceof ArrayInitializer) {
|
||||
arrayNode = (ArrayInitializer) node;
|
||||
}
|
||||
else {
|
||||
node = node.getParent();
|
||||
}
|
||||
}
|
||||
|
||||
if (arrayNode != null) {
|
||||
List<?> expressions = arrayNode.expressions();
|
||||
for (Object expression : expressions) {
|
||||
if (expression instanceof StringLiteral) {
|
||||
StringLiteral stringExr = (StringLiteral) expression;
|
||||
String value = stringExr.getLiteralValue();
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2024 Broadcom
|
||||
* 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
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Broadcom - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.beans;
|
||||
|
||||
import org.eclipse.lsp4j.CompletionItemKind;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
|
||||
import org.springframework.ide.vscode.commons.util.Renderable;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class QualifierCompletionProposal implements ICompletionProposal {
|
||||
|
||||
private static final String EMPTY_DETAIL = "";
|
||||
|
||||
private DocumentEdits edits;
|
||||
private String label;
|
||||
private String detail;
|
||||
private Renderable documentation;
|
||||
|
||||
public QualifierCompletionProposal(DocumentEdits edits, String label, String detail, Renderable documentation) {
|
||||
this.edits = edits;
|
||||
this.label = label;
|
||||
// PT 161489998 - Detail for proposal must not be null. For some clients like Eclipse,
|
||||
// a null detail results in an NPE at JDT level when inserting the proposal in the editor, and results
|
||||
// in odd behaviour like insertion of an extra new line.
|
||||
this.detail = detail == null ? EMPTY_DETAIL : detail;
|
||||
this.documentation = documentation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return this.label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletionItemKind getKind() {
|
||||
return CompletionItemKind.Value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DocumentEdits getTextEdit() {
|
||||
return this.edits;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDetail() {
|
||||
return this.detail;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Renderable getDocumentation() {
|
||||
return this.documentation;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2024 Broadcom
|
||||
* 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
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Broadcom - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.beans;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
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.CompilationUnit;
|
||||
import org.eclipse.jdt.core.dom.IAnnotationBinding;
|
||||
import org.eclipse.jdt.core.dom.StringLiteral;
|
||||
import org.eclipse.lsp4j.LocationLink;
|
||||
import org.eclipse.lsp4j.jsonrpc.CancelChecker;
|
||||
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
|
||||
import org.springframework.ide.vscode.boot.java.Annotations;
|
||||
import org.springframework.ide.vscode.boot.java.IJavaDefinitionProvider;
|
||||
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class QualifierDefinitionProvider implements IJavaDefinitionProvider {
|
||||
|
||||
private final SpringMetamodelIndex springIndex;
|
||||
|
||||
public QualifierDefinitionProvider(SpringMetamodelIndex springIndex) {
|
||||
this.springIndex = springIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LocationLink> getDefinitions(CancelChecker cancelToken, IJavaProject project, CompilationUnit cu, ASTNode n) {
|
||||
if (n instanceof StringLiteral) {
|
||||
StringLiteral valueNode = (StringLiteral) n;
|
||||
|
||||
ASTNode parent = ASTUtils.getNearestAnnotationParent(valueNode);
|
||||
|
||||
if (parent != null && parent instanceof Annotation) {
|
||||
Annotation a = (Annotation) parent;
|
||||
IAnnotationBinding binding = a.resolveAnnotationBinding();
|
||||
if (binding != null && binding.getAnnotationType() != null && Annotations.QUALIFIER.equals(binding.getAnnotationType().getQualifiedName())) {
|
||||
String beanName = valueNode.getLiteralValue();
|
||||
|
||||
if (beanName != null && beanName.length() > 0) {
|
||||
return findBeansWithName(project, beanName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private List<LocationLink> findBeansWithName(IJavaProject project, String beanName) {
|
||||
Bean[] beans = this.springIndex.getBeansWithName(project.getElementName(), beanName);
|
||||
|
||||
return Arrays.stream(beans)
|
||||
.map(bean -> {
|
||||
return new LocationLink(bean.getLocation().getUri(), bean.getLocation().getRange(), bean.getLocation().getRange());
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,9 +10,11 @@
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.data;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
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.Location;
|
||||
@@ -30,6 +32,7 @@ import org.springframework.ide.vscode.boot.java.handlers.SymbolAddOnInformation;
|
||||
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
|
||||
import org.springframework.ide.vscode.boot.java.utils.CachedSymbol;
|
||||
import org.springframework.ide.vscode.boot.java.utils.SpringIndexerJavaContext;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.AnnotationMetadata;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.InjectionPoint;
|
||||
import org.springframework.ide.vscode.commons.util.BadLocationException;
|
||||
@@ -73,7 +76,14 @@ public class DataRepositorySymbolProvider extends AbstractSymbolProvider {
|
||||
ASTUtils.findSupertypes(concreteBeanTypeBindung, supertypes);
|
||||
|
||||
String concreteRepoType = concreteBeanTypeBindung.getQualifiedName();
|
||||
Bean beanDefinition = new Bean(beanName, concreteRepoType, location, injectionPoints, supertypes, new String[0]);
|
||||
|
||||
Collection<Annotation> annotationsOnMethod = ASTUtils.getAnnotations(typeDeclaration);
|
||||
AnnotationMetadata[] annotations = annotationsOnMethod.stream()
|
||||
.map(an -> an.resolveAnnotationBinding())
|
||||
.map(t -> new AnnotationMetadata(t.getAnnotationType().getQualifiedName(), false, ASTUtils.getAttributes(t)))
|
||||
.toArray(AnnotationMetadata[]::new);
|
||||
|
||||
Bean beanDefinition = new Bean(beanName, concreteRepoType, location, injectionPoints, supertypes, annotations);
|
||||
|
||||
context.getGeneratedSymbols().add(new CachedSymbol(context.getDocURI(), context.getLastModified(), enhancedSymbol));
|
||||
context.getBeans().add(new CachedBean(context.getDocURI(), beanDefinition));
|
||||
|
||||
@@ -24,7 +24,6 @@ import org.eclipse.jdt.core.dom.Expression;
|
||||
import org.eclipse.jdt.core.dom.IAnnotationBinding;
|
||||
import org.eclipse.jdt.core.dom.IMemberValuePairBinding;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.IVariableBinding;
|
||||
import org.eclipse.jdt.core.dom.MemberValuePair;
|
||||
import org.eclipse.jdt.core.dom.MethodDeclaration;
|
||||
import org.eclipse.jdt.core.dom.NormalAnnotation;
|
||||
@@ -186,40 +185,17 @@ public class RequestMappingSymbolProvider extends AbstractSymbolProvider {
|
||||
IAnnotationBinding annotationBinding = getAnnotationFromSupertypes(node, context);
|
||||
IMemberValuePairBinding valuePair = getValuePair(annotationBinding, attributeNames);
|
||||
|
||||
if (valuePair != null) {
|
||||
Object value = valuePair.getValue();
|
||||
if (value instanceof Object[]) {
|
||||
Object[] values = (Object[]) value;
|
||||
String[] result = new String[values.length];
|
||||
for (int k = 0; k < result.length; k++) {
|
||||
|
||||
Object v = values[k];
|
||||
if (v instanceof IVariableBinding) {
|
||||
IVariableBinding varBinding = (IVariableBinding) v;
|
||||
result[k] = varBinding.getName();
|
||||
|
||||
ITypeBinding klass = varBinding.getDeclaringClass();
|
||||
if (klass!=null) {
|
||||
context.addDependency(klass);
|
||||
}
|
||||
|
||||
}
|
||||
else if (v instanceof String) {
|
||||
result[k] = (String) v;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
ASTUtils.MemberValuePairAndType result = ASTUtils.getValuesFromValuePair(valuePair);
|
||||
|
||||
if (result != null) {
|
||||
if (result.dereferencedType != null) {
|
||||
context.addDependency(result.dereferencedType);
|
||||
}
|
||||
else if (value instanceof String[]) {
|
||||
return (String[]) value;
|
||||
}
|
||||
else if (value != null) {
|
||||
return new String[] {value.toString()};
|
||||
}
|
||||
return result.values;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private IMemberValuePairBinding getValuePair(IAnnotationBinding annotationBinding, Set<String> names) {
|
||||
|
||||
@@ -13,7 +13,9 @@ package org.springframework.ide.vscode.boot.java.utils;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
@@ -26,7 +28,9 @@ import org.eclipse.jdt.core.dom.ArrayInitializer;
|
||||
import org.eclipse.jdt.core.dom.CompilationUnit;
|
||||
import org.eclipse.jdt.core.dom.Expression;
|
||||
import org.eclipse.jdt.core.dom.FieldDeclaration;
|
||||
import org.eclipse.jdt.core.dom.IAnnotationBinding;
|
||||
import org.eclipse.jdt.core.dom.IBinding;
|
||||
import org.eclipse.jdt.core.dom.IMemberValuePairBinding;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.IVariableBinding;
|
||||
import org.eclipse.jdt.core.dom.MemberValuePair;
|
||||
@@ -47,6 +51,7 @@ import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ide.vscode.boot.java.Annotations;
|
||||
import org.springframework.ide.vscode.boot.java.jdt.imports.ImportRewrite;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.AnnotationMetadata;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.DefaultValues;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.InjectionPoint;
|
||||
import org.springframework.ide.vscode.commons.util.BadLocationException;
|
||||
@@ -256,8 +261,15 @@ public class ASTUtils {
|
||||
}
|
||||
|
||||
|
||||
public static Collection<Annotation> getAnnotations(TypeDeclaration declaringType) {
|
||||
Object modifiersObj = declaringType.getStructuralProperty(TypeDeclaration.MODIFIERS2_PROPERTY);
|
||||
public static Collection<Annotation> getAnnotations(TypeDeclaration typeDeclaration) {
|
||||
return getAnnotationsFromModifiers(typeDeclaration.getStructuralProperty(TypeDeclaration.MODIFIERS2_PROPERTY));
|
||||
}
|
||||
|
||||
public static Collection<Annotation> getAnnotations(MethodDeclaration methodDeclaration) {
|
||||
return getAnnotationsFromModifiers(methodDeclaration.getStructuralProperty(MethodDeclaration.MODIFIERS2_PROPERTY));
|
||||
}
|
||||
|
||||
private static Collection<Annotation> getAnnotationsFromModifiers(Object modifiersObj) {
|
||||
if (modifiersObj instanceof List) {
|
||||
ImmutableList.Builder<Annotation> annotations = ImmutableList.builder();
|
||||
for (Object node : (List<?>)modifiersObj) {
|
||||
@@ -335,13 +347,17 @@ public class ASTUtils {
|
||||
if (object instanceof VariableDeclaration) {
|
||||
VariableDeclaration variable = (VariableDeclaration) object;
|
||||
String name = variable.getName().toString();
|
||||
String type = variable.resolveBinding().getType().getQualifiedName();
|
||||
|
||||
IVariableBinding variableBinding = variable.resolveBinding();
|
||||
String type = variableBinding.getType().getQualifiedName();
|
||||
|
||||
DocumentRegion region = ASTUtils.nodeRegion(doc, variable.getName());
|
||||
Range range = doc.toRange(region);
|
||||
|
||||
Location location = new Location(doc.getUri(), range);
|
||||
result.add(new InjectionPoint(name, type, location));
|
||||
AnnotationMetadata[] annotations = ASTUtils.getAnnotationsMetadata(variableBinding.getAnnotations());
|
||||
|
||||
result.add(new InjectionPoint(name, type, location, annotations));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -392,13 +408,17 @@ public class ASTUtils {
|
||||
if (object instanceof VariableDeclaration) {
|
||||
VariableDeclaration variable = (VariableDeclaration) object;
|
||||
String name = variable.getName().toString();
|
||||
String type = variable.resolveBinding().getType().getQualifiedName();
|
||||
|
||||
IVariableBinding variableBinding = variable.resolveBinding();
|
||||
String type = variableBinding.getType().getQualifiedName();
|
||||
|
||||
DocumentRegion region = ASTUtils.nodeRegion(doc, variable.getName());
|
||||
Range range = doc.toRange(region);
|
||||
|
||||
Location location = new Location(doc.getUri(), range);
|
||||
result.add(new InjectionPoint(name, type, location));
|
||||
AnnotationMetadata[] annotations = ASTUtils.getAnnotationsMetadata(variableBinding.getAnnotations());
|
||||
|
||||
result.add(new InjectionPoint(name, type, location, annotations));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -419,11 +439,14 @@ public class ASTUtils {
|
||||
for (FieldDeclaration field : fields) {
|
||||
|
||||
boolean autowiredField = false;
|
||||
|
||||
List<Annotation> fieldAnnotations = new ArrayList<>();
|
||||
|
||||
List<?> modifiers = field.modifiers();
|
||||
for (Object modifier : modifiers) {
|
||||
if (modifier instanceof Annotation) {
|
||||
Annotation annotation = (Annotation) modifier;
|
||||
fieldAnnotations.add(annotation);
|
||||
|
||||
String qualifiedName = annotation.resolveTypeBinding().getQualifiedName();
|
||||
if (Annotations.AUTOWIRED.equals(qualifiedName)) {
|
||||
@@ -445,7 +468,12 @@ public class ASTUtils {
|
||||
|
||||
String fieldType = field.getType().resolveBinding().getQualifiedName();
|
||||
|
||||
result.add(new InjectionPoint(fieldName, fieldType, fieldLocation));
|
||||
AnnotationMetadata[] annotationsMetadata = fieldAnnotations.stream()
|
||||
.map(an -> an.resolveAnnotationBinding())
|
||||
.map(t -> new AnnotationMetadata(t.getAnnotationType().getQualifiedName(), false, ASTUtils.getAttributes(t)))
|
||||
.toArray(AnnotationMetadata[]::new);
|
||||
|
||||
result.add(new InjectionPoint(fieldName, fieldType, fieldLocation, annotationsMetadata));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -454,11 +482,82 @@ public class ASTUtils {
|
||||
return result.size() > 0 ? result.toArray(new InjectionPoint[result.size()]) : DefaultValues.EMPTY_INJECTION_POINTS;
|
||||
}
|
||||
|
||||
private static AnnotationMetadata[] getAnnotationsMetadata(IAnnotationBinding[] annotations) {
|
||||
return Arrays.stream(annotations)
|
||||
.map(t -> new AnnotationMetadata(t.getAnnotationType().getQualifiedName(), false, getAttributes(t)))
|
||||
.toArray(AnnotationMetadata[]::new);
|
||||
}
|
||||
|
||||
public static Map<String, String[]> getAttributes(IAnnotationBinding t) {
|
||||
Map<String, String[]> result = new LinkedHashMap<>();
|
||||
|
||||
IMemberValuePairBinding[] pairs = t.getDeclaredMemberValuePairs();
|
||||
for (IMemberValuePairBinding pair : pairs) {
|
||||
MemberValuePairAndType values = ASTUtils.getValuesFromValuePair(pair);
|
||||
if (values != null) {
|
||||
result.put(pair.getName(), values.values);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static ASTNode getNearestAnnotationParent(ASTNode node) {
|
||||
while (node != null && !(node instanceof Annotation)) {
|
||||
node = node.getParent();
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
public static MemberValuePairAndType getValuesFromValuePair(IMemberValuePairBinding valuePair) {
|
||||
if (valuePair != null) {
|
||||
Object value = valuePair.getValue();
|
||||
|
||||
MemberValuePairAndType result = new MemberValuePairAndType();
|
||||
|
||||
if (value instanceof Object[]) {
|
||||
Object[] values = (Object[]) value;
|
||||
result.values = new String[values.length];
|
||||
for (int k = 0; k < values.length; k++) {
|
||||
|
||||
Object v = values[k];
|
||||
if (v instanceof IVariableBinding) {
|
||||
IVariableBinding varBinding = (IVariableBinding) v;
|
||||
result.values[k] = varBinding.getName();
|
||||
|
||||
ITypeBinding klass = varBinding.getDeclaringClass();
|
||||
if (klass != null) {
|
||||
result.dereferencedType= klass;
|
||||
}
|
||||
|
||||
}
|
||||
else if (v instanceof String) {
|
||||
result.values[k] = (String) v;
|
||||
}
|
||||
else if (v instanceof ITypeBinding) {
|
||||
result.values[k] = ((ITypeBinding) v).getQualifiedName();
|
||||
}
|
||||
else if (v != null) {
|
||||
result.values[k] = v.toString();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else if (value instanceof String[]) {
|
||||
result.values = (String[]) value;
|
||||
return result;
|
||||
}
|
||||
else if (value != null) {
|
||||
result.values = new String[] {value.toString()};
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static class MemberValuePairAndType {
|
||||
public String[] values;
|
||||
public ITypeBinding dereferencedType;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,7 +18,9 @@ import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.assertj.core.util.Arrays;
|
||||
@@ -28,6 +30,7 @@ import org.eclipse.lsp4j.Range;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
|
||||
import org.springframework.ide.vscode.boot.index.cache.IndexCacheOnDisc;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.AnnotationMetadata;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.DefaultValues;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.InjectionPoint;
|
||||
@@ -38,7 +41,9 @@ public class SpringMetamodelIndexTest {
|
||||
|
||||
private InjectionPoint[] emptyInjectionPoints = new InjectionPoint[0];
|
||||
private Set<String> emptySupertypes = new HashSet<>();
|
||||
private String[] emptyAnnotations = new String[0];
|
||||
private AnnotationMetadata[] emptyAnnotations = new AnnotationMetadata[0];
|
||||
private AnnotationMetadata[] emptyInjectionAnnotations = new AnnotationMetadata[0];
|
||||
private Map<String, String[]> emptyAnnotationAttributes = new LinkedHashMap<>();
|
||||
|
||||
private Location locationForDoc1 = new Location("docURI1", new Range(new Position(1, 1), new Position(1, 10)));
|
||||
private Location locationForDoc2 = new Location("docURI2", new Range(new Position(2, 1), new Position(2, 10)));
|
||||
@@ -220,8 +225,10 @@ public class SpringMetamodelIndexTest {
|
||||
|
||||
@Test
|
||||
void testOverallSerializeDeserializeBeans() {
|
||||
InjectionPoint point1 = new InjectionPoint("point1", "point1-type", locationForDoc2);
|
||||
InjectionPoint point2 = new InjectionPoint("point2", "point2-type", locationForDoc1);
|
||||
InjectionPoint point1 = new InjectionPoint("point1", "point1-type", locationForDoc2, new AnnotationMetadata[]
|
||||
{new AnnotationMetadata("anno1", false, emptyAnnotationAttributes),new AnnotationMetadata("anno2", false, emptyAnnotationAttributes)});
|
||||
|
||||
InjectionPoint point2 = new InjectionPoint("point2", "point2-type", locationForDoc1, null);
|
||||
|
||||
Bean bean1 = new Bean("beanName1", "beanType", locationForDoc1, new InjectionPoint[] {point1, point2}, Set.of("supertype1", "supertype2"), emptyAnnotations);
|
||||
String serialized = bean1.toString();
|
||||
@@ -247,6 +254,12 @@ public class SpringMetamodelIndexTest {
|
||||
assertTrue(deserializedBean.isTypeCompatibleWith("supertype1"));
|
||||
assertTrue(deserializedBean.isTypeCompatibleWith("supertype2"));
|
||||
assertFalse(deserializedBean.isTypeCompatibleWith("java.lang.String"));
|
||||
|
||||
assertEquals(2, points[0].getAnnotations().length);
|
||||
assertEquals("anno1", points[0].getAnnotations()[0].getAnnotationType());
|
||||
assertEquals("anno2", points[0].getAnnotations()[1].getAnnotationType());
|
||||
|
||||
assertEquals(0, points[1].getAnnotations().length);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -270,6 +283,12 @@ public class SpringMetamodelIndexTest {
|
||||
assertSame(DefaultValues.EMPTY_INJECTION_POINTS, bean1.getInjectionPoints());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEmptyAnnotationOptimization() {
|
||||
InjectionPoint point = new InjectionPoint("pointName", "pointType", locationForDoc1, emptyInjectionAnnotations);
|
||||
assertSame(DefaultValues.EMPTY_ANNOTATIONS, point.getAnnotations());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFindNoMatchingBeansWithEmptySupertypes() {
|
||||
SpringMetamodelIndex index = new SpringMetamodelIndex();
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.index.test;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
@@ -17,6 +18,7 @@ import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@@ -34,6 +36,7 @@ import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest;
|
||||
import org.springframework.ide.vscode.boot.bootiful.SymbolProviderTestConf;
|
||||
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.AnnotationMetadata;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.DefaultValues;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.InjectionPoint;
|
||||
@@ -90,7 +93,7 @@ public class SpringMetamodelIndexerBeansTest {
|
||||
Bean[] beans = springIndex.getBeansWithName("test-spring-indexing", "bean1");
|
||||
|
||||
String docUri = directory.toPath().resolve("src/main/java/org/test/MainClass.java").toUri().toString();
|
||||
Location location = new Location(docUri, new Range(new Position(13, 1), new Position(13, 6)));
|
||||
Location location = new Location(docUri, new Range(new Position(15, 1), new Position(15, 6)));
|
||||
assertEquals(location, beans[0].getLocation());
|
||||
}
|
||||
|
||||
@@ -194,12 +197,12 @@ public class SpringMetamodelIndexerBeansTest {
|
||||
|
||||
assertEquals("bean1", injectionPoints[0].getName());
|
||||
assertEquals("org.test.BeanClass1", injectionPoints[0].getType());
|
||||
Location ip1Location = new Location(docUri, new Range(new Position(10, 31), new Position(10, 36)));
|
||||
Location ip1Location = new Location(docUri, new Range(new Position(12, 20), new Position(12, 25)));
|
||||
assertEquals(ip1Location, injectionPoints[0].getLocation());
|
||||
|
||||
assertEquals("bean2", injectionPoints[1].getName());
|
||||
assertEquals("org.test.BeanClass2", injectionPoints[1].getType());
|
||||
Location ip2Location = new Location(docUri, new Range(new Position(11, 31), new Position(11, 36)));
|
||||
Location ip2Location = new Location(docUri, new Range(new Position(16, 20), new Position(16, 25)));
|
||||
assertEquals(ip2Location, injectionPoints[1].getLocation());
|
||||
}
|
||||
|
||||
@@ -208,13 +211,15 @@ public class SpringMetamodelIndexerBeansTest {
|
||||
Bean[] beans = springIndex.getBeansWithName("test-spring-indexing", "customerRepository");
|
||||
|
||||
assertEquals(1, beans.length);
|
||||
assertEquals("customerRepository", beans[0].getName());
|
||||
assertEquals("org.test.springdata.CustomerRepository", beans[0].getType());
|
||||
|
||||
assertTrue(beans[0].isTypeCompatibleWith("org.test.springdata.CustomerRepository"));
|
||||
assertTrue(beans[0].isTypeCompatibleWith("org.springframework.data.repository.CrudRepository"));
|
||||
Bean repositoryBean = beans[0];
|
||||
|
||||
InjectionPoint[] injectionPoints = beans[0].getInjectionPoints();
|
||||
assertEquals("customerRepository", repositoryBean.getName());
|
||||
assertEquals("org.test.springdata.CustomerRepository", repositoryBean.getType());
|
||||
|
||||
assertTrue(repositoryBean.isTypeCompatibleWith("org.test.springdata.CustomerRepository"));
|
||||
assertTrue(repositoryBean.isTypeCompatibleWith("org.springframework.data.repository.CrudRepository"));
|
||||
|
||||
InjectionPoint[] injectionPoints = repositoryBean.getInjectionPoints();
|
||||
assertEquals(0, injectionPoints.length);
|
||||
assertSame(DefaultValues.EMPTY_INJECTION_POINTS, injectionPoints);
|
||||
}
|
||||
@@ -235,5 +240,193 @@ public class SpringMetamodelIndexerBeansTest {
|
||||
assertFalse(beans[0].isTypeCompatibleWith("java.lang.String"));
|
||||
assertFalse(beans[0].isTypeCompatibleWith("java.util.Comparator"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAnnotationMetadataFromComponentBeans() {
|
||||
Bean[] beans = springIndex.getBeansWithName("test-spring-indexing", "mainClass");
|
||||
assertEquals(1, beans.length);
|
||||
|
||||
Bean mainClassBean = beans[0];
|
||||
AnnotationMetadata[] annotations = mainClassBean.getAnnotations();
|
||||
|
||||
assertEquals(4, annotations.length);
|
||||
assertEquals("org.springframework.boot.autoconfigure.SpringBootApplication", annotations[0].getAnnotationType());
|
||||
assertFalse(annotations[0].isMetaAnnotation());
|
||||
|
||||
assertEquals("org.springframework.boot.SpringBootConfiguration", annotations[1].getAnnotationType());
|
||||
assertTrue(annotations[1].isMetaAnnotation());
|
||||
|
||||
assertEquals("org.springframework.context.annotation.Configuration", annotations[2].getAnnotationType());
|
||||
assertTrue(annotations[2].isMetaAnnotation());
|
||||
|
||||
assertEquals("org.springframework.stereotype.Component", annotations[3].getAnnotationType());
|
||||
assertTrue(annotations[3].isMetaAnnotation());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAnnotationMetadataFromBeanMethodBean() {
|
||||
Bean[] beans = springIndex.getBeansWithName("test-spring-indexing", "bean3");
|
||||
assertEquals(1, beans.length);
|
||||
|
||||
Bean mainClassBean = beans[0];
|
||||
AnnotationMetadata[] annotations = mainClassBean.getAnnotations();
|
||||
|
||||
assertEquals(3, annotations.length);
|
||||
|
||||
AnnotationMetadata beanAnnotation = annotations[0];
|
||||
assertEquals("org.springframework.context.annotation.Bean", beanAnnotation.getAnnotationType());
|
||||
assertFalse(annotations[0].isMetaAnnotation());
|
||||
assertEquals(0, annotations[0].getAttributes().size());
|
||||
|
||||
AnnotationMetadata qualifierAnnotation = annotations[1];
|
||||
assertEquals("org.springframework.beans.factory.annotation.Qualifier", qualifierAnnotation.getAnnotationType());
|
||||
Map<String, String[]> attributes = qualifierAnnotation.getAttributes();
|
||||
assertEquals(1, attributes.size());
|
||||
assertTrue(attributes.containsKey("value"));
|
||||
assertArrayEquals(new String[] {"qualifier1"}, attributes.get("value"));
|
||||
|
||||
AnnotationMetadata profileAnnotation = annotations[2];
|
||||
assertEquals("org.springframework.context.annotation.Profile", profileAnnotation.getAnnotationType());
|
||||
assertFalse(profileAnnotation.isMetaAnnotation());
|
||||
|
||||
attributes = profileAnnotation.getAttributes();
|
||||
assertEquals(1, attributes.size());
|
||||
assertTrue(attributes.containsKey("value"));
|
||||
assertArrayEquals(new String[] {"testprofile","testprofile2"}, attributes.get("value"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAnnotationMetadataFromBeanMethodWithInjectionPointAnnotations() {
|
||||
Bean[] beans = springIndex.getBeansWithName("test-spring-indexing", "beanWithAnnotationsOnInjectionPoints");
|
||||
assertEquals(1, beans.length);
|
||||
|
||||
Bean bean = beans[0];
|
||||
AnnotationMetadata[] annotations = bean.getAnnotations();
|
||||
|
||||
assertEquals(2, annotations.length);
|
||||
|
||||
AnnotationMetadata beanAnnotation = annotations[0];
|
||||
AnnotationMetadata dependsonAnnotation = annotations[1];
|
||||
|
||||
assertEquals("org.springframework.context.annotation.Bean", beanAnnotation.getAnnotationType());
|
||||
assertEquals("org.springframework.context.annotation.DependsOn", dependsonAnnotation.getAnnotationType());
|
||||
|
||||
Map<String, String[]> dependsOnAttributes = dependsonAnnotation.getAttributes();
|
||||
assertEquals(1, dependsOnAttributes.size());
|
||||
assertArrayEquals(new String[] {"bean1", "bean2"}, dependsOnAttributes.get("value"));
|
||||
|
||||
InjectionPoint[] injectionPoints = bean.getInjectionPoints();
|
||||
assertEquals(2, injectionPoints.length);
|
||||
|
||||
AnnotationMetadata[] annotationsFromPoint1 = injectionPoints[0].getAnnotations();
|
||||
AnnotationMetadata[] annotationsFromPoint2 = injectionPoints[1].getAnnotations();
|
||||
|
||||
assertEquals(1, annotationsFromPoint1.length);
|
||||
assertEquals(1, annotationsFromPoint2.length);
|
||||
|
||||
assertEquals("org.springframework.beans.factory.annotation.Qualifier", annotationsFromPoint1[0].getAnnotationType());
|
||||
Map<String, String[]> attributesFromParam1 = annotationsFromPoint1[0].getAttributes();
|
||||
assertEquals(1, attributesFromParam1.size());
|
||||
assertArrayEquals(new String[] {"q1"}, attributesFromParam1.get("value"));
|
||||
|
||||
assertEquals("org.springframework.beans.factory.annotation.Qualifier", annotationsFromPoint2[0].getAnnotationType());
|
||||
Map<String, String[]> attributesFromParam2 = annotationsFromPoint2[0].getAttributes();
|
||||
assertEquals(1, attributesFromParam2.size());
|
||||
assertArrayEquals(new String[] {"q2"}, attributesFromParam2.get("value"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAnnotationMetadataFromComponentClass() {
|
||||
Bean[] beans = springIndex.getBeansWithName("test-spring-indexing", "configurationWithInjectionsAndAnnotations");
|
||||
assertEquals(1, beans.length);
|
||||
|
||||
Bean bean = beans[0];
|
||||
|
||||
InjectionPoint[] injectionPoints = bean.getInjectionPoints();
|
||||
assertEquals(0, injectionPoints.length);
|
||||
|
||||
AnnotationMetadata[] annotations = bean.getAnnotations();
|
||||
|
||||
assertEquals(4, annotations.length);
|
||||
|
||||
AnnotationMetadata configurationAnnotation= annotations[0];
|
||||
AnnotationMetadata qualifierAnnotation = annotations[1];
|
||||
AnnotationMetadata runtimeHintsAnnotation = annotations[2];
|
||||
AnnotationMetadata componentMetaAnnotation = annotations[3];
|
||||
|
||||
assertEquals("org.springframework.context.annotation.Configuration", configurationAnnotation.getAnnotationType());
|
||||
assertEquals("org.springframework.beans.factory.annotation.Qualifier", qualifierAnnotation.getAnnotationType());
|
||||
assertEquals("org.springframework.context.annotation.ImportRuntimeHints", runtimeHintsAnnotation.getAnnotationType());
|
||||
assertEquals("org.springframework.stereotype.Component", componentMetaAnnotation.getAnnotationType());
|
||||
|
||||
Map<String, String[]> qualifierAttributes = qualifierAnnotation.getAttributes();
|
||||
assertEquals(1, qualifierAttributes.size());
|
||||
assertArrayEquals(new String[] {"qualifier"}, qualifierAttributes.get("value"));
|
||||
|
||||
Map<String, String[]> runtimeHintsAttributes = runtimeHintsAnnotation.getAttributes();
|
||||
assertEquals(1, runtimeHintsAttributes.size());
|
||||
assertArrayEquals(new String[] {"org.test.MainClass"}, runtimeHintsAttributes.get("value"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAnnotationMetadataFromInjectionPointsFromAutowiredFields() {
|
||||
Bean[] beans = springIndex.getBeansWithName("test-spring-indexing", "autowiredInjectionService");
|
||||
assertEquals(1, beans.length);
|
||||
|
||||
InjectionPoint[] injectionPoints = beans[0].getInjectionPoints();
|
||||
assertEquals(2, injectionPoints.length);
|
||||
|
||||
AnnotationMetadata[] annotationsPoint1 = injectionPoints[0].getAnnotations();
|
||||
assertEquals(1, annotationsPoint1.length);
|
||||
assertEquals("org.springframework.beans.factory.annotation.Autowired", annotationsPoint1[0].getAnnotationType());
|
||||
assertFalse(annotationsPoint1[0].isMetaAnnotation());
|
||||
assertEquals(0, annotationsPoint1[0].getAttributes().size());
|
||||
|
||||
AnnotationMetadata[] annotationsPoint2 = injectionPoints[1].getAnnotations();
|
||||
assertEquals(2, annotationsPoint2.length);
|
||||
|
||||
AnnotationMetadata autowiredFromPoint2 = annotationsPoint2[0];
|
||||
assertEquals("org.springframework.beans.factory.annotation.Autowired", autowiredFromPoint2.getAnnotationType());
|
||||
assertFalse(autowiredFromPoint2.isMetaAnnotation());
|
||||
assertEquals(0, autowiredFromPoint2.getAttributes().size());
|
||||
|
||||
AnnotationMetadata qualifierFromPoint2 = annotationsPoint2[1];
|
||||
assertEquals("org.springframework.beans.factory.annotation.Qualifier", qualifierFromPoint2.getAnnotationType());
|
||||
assertFalse(qualifierFromPoint2.isMetaAnnotation());
|
||||
assertEquals(1, qualifierFromPoint2.getAttributes().size());
|
||||
|
||||
Map<String, String[]> qualifierAttributes = qualifierFromPoint2.getAttributes();
|
||||
assertEquals(1, qualifierAttributes.size());
|
||||
assertArrayEquals(new String[] {"qual1"}, qualifierAttributes.get("value"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAnnotationMetadataFromSpringDataRepository() {
|
||||
Bean[] beans = springIndex.getBeansWithName("test-spring-indexing", "customerRepository");
|
||||
|
||||
assertEquals(1, beans.length);
|
||||
|
||||
AnnotationMetadata[] annotations = beans[0].getAnnotations();
|
||||
assertEquals(2, annotations.length);
|
||||
|
||||
AnnotationMetadata qualifierAnnotation = annotations[0];
|
||||
AnnotationMetadata profileAnnotation = annotations[1];
|
||||
|
||||
assertEquals("org.springframework.beans.factory.annotation.Qualifier", qualifierAnnotation.getAnnotationType());
|
||||
assertFalse(qualifierAnnotation.isMetaAnnotation());
|
||||
|
||||
Map<String, String[]> qualifierAttributes = qualifierAnnotation.getAttributes();
|
||||
assertEquals(1, qualifierAttributes.size());
|
||||
assertArrayEquals(new String[] {"repoQualifier"}, qualifierAttributes.get("value"));
|
||||
|
||||
assertEquals("org.springframework.context.annotation.Profile", profileAnnotation.getAnnotationType());
|
||||
assertFalse(profileAnnotation.isMetaAnnotation());
|
||||
|
||||
Map<String, String[]> profileAttributes = profileAnnotation.getAttributes();
|
||||
assertEquals(1, profileAttributes.size());
|
||||
assertArrayEquals(new String[] {"prof1", "prof2"}, profileAttributes.get("value"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -165,7 +165,7 @@ public class DependsOnCompletionProviderTest {
|
||||
assertCompletions("@DependsOn({\"bean1\",<*>\"bean2\"})", 1, "@DependsOn({\"bean1\",\"bean3\",<*>\"bean2\"})");
|
||||
}
|
||||
|
||||
private void assertCompletions(String completionLine, int noOfExcpectedCompletions, String expectedCompletedLine) throws Exception {
|
||||
private void assertCompletions(String completionLine, int noOfExpectedCompletions, String expectedCompletedLine) throws Exception {
|
||||
String editorContent = """
|
||||
package org.test;
|
||||
|
||||
@@ -182,9 +182,9 @@ public class DependsOnCompletionProviderTest {
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA, editorContent, tempJavaDocUri);
|
||||
|
||||
List<CompletionItem> completions = editor.getCompletions();
|
||||
assertEquals(noOfExcpectedCompletions, completions.size());
|
||||
assertEquals(noOfExpectedCompletions, completions.size());
|
||||
|
||||
if (noOfExcpectedCompletions > 0) {
|
||||
if (noOfExpectedCompletions > 0) {
|
||||
editor.apply(completions.get(0));
|
||||
assertEquals("""
|
||||
package org.test;
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2024 Broadcom
|
||||
* 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
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Broadcom - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.beans.test;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.lsp4j.CompletionItem;
|
||||
import org.eclipse.lsp4j.Location;
|
||||
import org.eclipse.lsp4j.Position;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.ide.vscode.boot.app.SpringSymbolIndex;
|
||||
import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest;
|
||||
import org.springframework.ide.vscode.boot.bootiful.SymbolProviderTestConf;
|
||||
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.AnnotationMetadata;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.Editor;
|
||||
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@BootLanguageServerTest
|
||||
@Import(SymbolProviderTestConf.class)
|
||||
public class QualifierCompletionProviderTest {
|
||||
|
||||
@Autowired private BootLanguageServerHarness harness;
|
||||
@Autowired private JavaProjectFinder projectFinder;
|
||||
@Autowired private SpringMetamodelIndex springIndex;
|
||||
@Autowired private SpringSymbolIndex indexer;
|
||||
|
||||
private File directory;
|
||||
private IJavaProject project;
|
||||
private Bean[] indexedBeans;
|
||||
private String tempJavaDocUri;
|
||||
private Bean bean1;
|
||||
private Bean bean2;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() throws Exception {
|
||||
harness.intialize(null);
|
||||
|
||||
directory = new File(ProjectsHarness.class.getResource("/test-projects/test-spring-indexing/").toURI());
|
||||
|
||||
String projectDir = directory.toURI().toString();
|
||||
project = projectFinder.find(new TextDocumentIdentifier(projectDir)).get();
|
||||
|
||||
CompletableFuture<Void> initProject = indexer.waitOperation();
|
||||
initProject.get(5, TimeUnit.SECONDS);
|
||||
|
||||
indexedBeans = springIndex.getBeansOfProject(project.getElementName());
|
||||
|
||||
tempJavaDocUri = directory.toPath().resolve("src/main/java/org/test/TempClass.java").toUri().toString();
|
||||
AnnotationMetadata annotationBean1 = new AnnotationMetadata("org.springframework.beans.factory.annotation.Qualifier", false, Map.of("value", new String[] {"quali1"}));
|
||||
AnnotationMetadata annotationBean2 = new AnnotationMetadata("org.springframework.beans.factory.annotation.Qualifier", false, Map.of("value", new String[] {"quali2"}));
|
||||
|
||||
bean1 = new Bean("bean1", "type1", new Location(tempJavaDocUri, new Range(new Position(1,1), new Position(1, 20))), null, null, new AnnotationMetadata[] {annotationBean1});
|
||||
bean2 = new Bean("bean2", "type2", new Location(tempJavaDocUri, new Range(new Position(1,1), new Position(1, 20))), null, null, new AnnotationMetadata[] {annotationBean2});
|
||||
|
||||
springIndex.updateBeans(project.getElementName(), new Bean[] {bean1, bean2});
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void restoreIndexState() {
|
||||
this.springIndex.updateBeans(project.getElementName(), indexedBeans);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQualifierCompletionWithoutQuotesWithoutPrefix() throws Exception {
|
||||
assertCompletions("@Qualifier(<*>)", 4, new String[] {"bean1", "bean2", "quali1", "quali2"}, 0, "@Qualifier(\"bean1\"<*>)");
|
||||
}
|
||||
|
||||
// @Test
|
||||
// public void testDependsOnCompletionWithoutQuotesWithPrefix() throws Exception {
|
||||
// assertCompletions("@DependsOn(be<*>)", 2, "@DependsOn(\"bean1\"<*>)");
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testDependsOnCompletionWithoutQuotesWithAttributeName() throws Exception {
|
||||
// assertCompletions("@DependsOn(value=<*>)", 2, "@DependsOn(value=\"bean1\"<*>)");
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testDependsOnCompletionInsideOfQuotesWithoutPrefix() throws Exception {
|
||||
// assertCompletions("@DependsOn(\"<*>\")", 2, "@DependsOn(\"bean1<*>\")");
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testDependsOnCompletionWithoutQuotesWithoutPrefixInsideArray() throws Exception {
|
||||
// assertCompletions("@DependsOn({<*>})", 2, "@DependsOn({\"bean1\"<*>})");
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testDependsOnCompletionInsideOfQuotesWithoutPrefixInsideArray() throws Exception {
|
||||
// assertCompletions("@DependsOn({\"<*>\"})", 2, "@DependsOn({\"bean1<*>\"})");
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testDependsOnCompletionInsideOfQuotesWithPrefix() throws Exception {
|
||||
// assertCompletions("@DependsOn(\"be<*>\")", 2, "@DependsOn(\"bean1<*>\")");
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testDependsOnCompletionInsideOfQuotesAndArrayWithPrefix() throws Exception {
|
||||
// assertCompletions("@DependsOn({\"be<*>\"})", 2, "@DependsOn({\"bean1<*>\"})");
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testDependsOnCompletionInsideOfQuotesWithPrefixButWithoutMatches() throws Exception {
|
||||
// assertCompletions("@DependsOn(\"XXX<*>\")", 0, null);
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testDependsOnCompletionOutsideOfAnnotation1() throws Exception {
|
||||
// assertCompletions("@DependsOn(\"XXX\")<*>", 0, null);
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testDependsOnCompletionOutsideOfAnnotation2() throws Exception {
|
||||
// assertCompletions("@DependsOn<*>(\"XXX\")", 0, null);
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testDependsOnCompletionInsideOfQuotesWithPrefixAndReplacedPostfix() throws Exception {
|
||||
// assertCompletions("@DependsOn(\"be<*>xxx\")", 2, "@DependsOn(\"bean1<*>xxx\")");
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testDependsOnCompletionInsideOfArrayBehindExistingElement() throws Exception {
|
||||
// assertCompletions("@DependsOn({\"bean1\",<*>})", 1, "@DependsOn({\"bean1\",\"bean2\"<*>})");
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testDependsOnCompletionInsideOfArrayInFrontOfExistingElement() throws Exception {
|
||||
// assertCompletions("@DependsOn({<*>\"bean1\"})", 1, "@DependsOn({\"bean2\",<*>\"bean1\"})");
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testDependsOnCompletionInsideOfArrayBetweenExistingElements() throws Exception {
|
||||
// Bean bean3 = new Bean("bean3", "type3", new Location(tempJavaDocUri, new Range(new Position(1,1), new Position(1, 20))), null, null, null);
|
||||
// springIndex.updateBeans(project.getElementName(), new Bean[] {bean1, bean2, bean3});
|
||||
//
|
||||
// assertCompletions("@DependsOn({\"bean1\",<*>\"bean2\"})", 1, "@DependsOn({\"bean1\",\"bean3\",<*>\"bean2\"})");
|
||||
// }
|
||||
|
||||
private void assertCompletions(String completionLine, int noOfExpectedCompletions, String expectedCompletedLine) throws Exception {
|
||||
assertCompletions(completionLine, noOfExpectedCompletions, null, 0, expectedCompletedLine);
|
||||
}
|
||||
|
||||
private void assertCompletions(String completionLine, int noOfExcpectedCompletions, String[] expectedCompletions, int chosenCompletion, String expectedCompletedLine) throws Exception {
|
||||
String editorContent = """
|
||||
package org.test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
||||
@Component
|
||||
""" +
|
||||
completionLine + "\n" +
|
||||
"""
|
||||
public class TestDependsOnClass {
|
||||
}
|
||||
""";
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA, editorContent, tempJavaDocUri);
|
||||
|
||||
List<CompletionItem> completions = editor.getCompletions();
|
||||
assertEquals(noOfExcpectedCompletions, completions.size());
|
||||
|
||||
if (expectedCompletions != null) {
|
||||
String[] completionItems = completions.stream()
|
||||
.map(item -> item.getLabel())
|
||||
.toArray(size -> new String[size]);
|
||||
|
||||
assertArrayEquals(expectedCompletions, completionItems);
|
||||
}
|
||||
|
||||
if (noOfExcpectedCompletions > 0) {
|
||||
editor.apply(completions.get(chosenCompletion));
|
||||
assertEquals("""
|
||||
package org.test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
||||
@Component
|
||||
""" + expectedCompletedLine + "\n" +
|
||||
"""
|
||||
public class TestDependsOnClass {
|
||||
}
|
||||
""", editor.getText());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2024 Broadcom
|
||||
* 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
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Broadcom - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.beans.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.lsp4j.LocationLink;
|
||||
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.ide.vscode.boot.app.SpringSymbolIndex;
|
||||
import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest;
|
||||
import org.springframework.ide.vscode.boot.bootiful.SymbolProviderTestConf;
|
||||
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.Editor;
|
||||
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@BootLanguageServerTest
|
||||
@Import(SymbolProviderTestConf.class)
|
||||
public class QualifierDefinitionProviderTest {
|
||||
|
||||
@Autowired private BootLanguageServerHarness harness;
|
||||
@Autowired private JavaProjectFinder projectFinder;
|
||||
@Autowired private SpringMetamodelIndex springIndex;
|
||||
@Autowired private SpringSymbolIndex indexer;
|
||||
|
||||
private File directory;
|
||||
private IJavaProject project;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() throws Exception {
|
||||
harness.intialize(null);
|
||||
|
||||
directory = new File(ProjectsHarness.class.getResource("/test-projects/test-spring-indexing/").toURI());
|
||||
|
||||
String projectDir = directory.toURI().toString();
|
||||
project = projectFinder.find(new TextDocumentIdentifier(projectDir)).get();
|
||||
|
||||
CompletableFuture<Void> initProject = indexer.waitOperation();
|
||||
initProject.get(5, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQualifierRefersToBeanDefinitionLink() throws Exception {
|
||||
String tempJavaDocUri = directory.toPath().resolve("src/main/java/org/test/TempClass.java").toUri().toString();
|
||||
|
||||
Editor editor = harness.newEditor(LanguageId.JAVA, """
|
||||
package org.test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
||||
@Component
|
||||
@Qualifier("bean1")
|
||||
public class TestDependsOnClass {
|
||||
}""", tempJavaDocUri);
|
||||
|
||||
String expectedDefinitionUri = directory.toPath().resolve("src/main/java/org/test/MainClass.java").toUri().toString();
|
||||
|
||||
Bean[] beans = springIndex.getBeansWithName(project.getElementName(), "bean1");
|
||||
assertEquals(1, beans.length);
|
||||
|
||||
LocationLink expectedLocation = new LocationLink(expectedDefinitionUri,
|
||||
beans[0].getLocation().getRange(), beans[0].getLocation().getRange(),
|
||||
null);
|
||||
|
||||
editor.assertLinkTargets("bean1", List.of(expectedLocation));
|
||||
}
|
||||
|
||||
// @Test
|
||||
// public void testMultipleDependsOnBeanDefinitionLink() throws Exception {
|
||||
// String tempJavaDocUri = directory.toPath().resolve("src/main/java/org/test/TempClass.java").toUri().toString();
|
||||
//
|
||||
// Editor editor = harness.newEditor(LanguageId.JAVA, """
|
||||
// package org.test;
|
||||
//
|
||||
// import org.springframework.context.annotation.DependsOn;
|
||||
//
|
||||
// @Component
|
||||
// @DependsOn({"bean1", "bean2"})
|
||||
// public class TestDependsOnClass {
|
||||
// }""", tempJavaDocUri);
|
||||
//
|
||||
// String expectedDefinitionUri = directory.toPath().resolve("src/main/java/org/test/MainClass.java").toUri().toString();
|
||||
//
|
||||
// Bean[] beans = springIndex.getBeansWithName(project.getElementName(), "bean1");
|
||||
// assertEquals(1, beans.length);
|
||||
//
|
||||
// LocationLink expectedLocation = new LocationLink(expectedDefinitionUri,
|
||||
// beans[0].getLocation().getRange(), beans[0].getLocation().getRange(),
|
||||
// null);
|
||||
//
|
||||
// editor.assertLinkTargets("bean1", List.of(expectedLocation));
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testDependsOnWithMultipleBeanDefinitionLinks() throws Exception {
|
||||
// String tempJavaDocUri = directory.toPath().resolve("src/main/java/org/test/TempClass.java").toUri().toString();
|
||||
//
|
||||
// Editor editor = harness.newEditor(LanguageId.JAVA, """
|
||||
// package org.test;
|
||||
//
|
||||
// import org.springframework.context.annotation.DependsOn;
|
||||
//
|
||||
// @Component
|
||||
// @DependsOn("bean1")
|
||||
// public class TestDependsOnClass {
|
||||
// }""", tempJavaDocUri);
|
||||
//
|
||||
// String expectedDefinitionUri = directory.toPath().resolve("src/main/java/org/test/MainClass.java").toUri().toString();
|
||||
//
|
||||
// List<Bean> beansOfDoc = new ArrayList<>(List.of(springIndex.getBeansOfDocument(expectedDefinitionUri)));
|
||||
// beansOfDoc.add(new Bean("bean1", "type", new Location(expectedDefinitionUri, new Range(new Position(20, 1), new Position(20, 10))), null, null, null));
|
||||
// springIndex.updateBeans(project.getElementName(), expectedDefinitionUri, beansOfDoc.toArray(new Bean[0]));
|
||||
//
|
||||
// Bean[] beans = springIndex.getBeansWithName(project.getElementName(), "bean1");
|
||||
// assertEquals(2, beans.length);
|
||||
//
|
||||
// LocationLink expectedLocation1 = new LocationLink(expectedDefinitionUri,
|
||||
// beans[0].getLocation().getRange(), beans[0].getLocation().getRange(),
|
||||
// null);
|
||||
//
|
||||
// LocationLink expectedLocation2 = new LocationLink(expectedDefinitionUri,
|
||||
// beans[1].getLocation().getRange(), beans[1].getLocation().getRange(),
|
||||
// null);
|
||||
//
|
||||
// editor.assertLinkTargets("bean1", List.of(expectedLocation1, expectedLocation2));
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -3,6 +3,8 @@ package org.test;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
||||
@SpringBootApplication
|
||||
public class MainClass {
|
||||
@@ -21,4 +23,11 @@ public class MainClass {
|
||||
return new BeanClass2();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Qualifier("qualifier1")
|
||||
@Profile({"testprofile","testprofile2"})
|
||||
BeanClass2 bean3() {
|
||||
return new BeanClass2();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.test.injections;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.test.BeanClass1;
|
||||
import org.test.BeanClass2;
|
||||
@@ -8,8 +9,12 @@ import org.test.BeanClass2;
|
||||
@Service
|
||||
public class AutowiredInjectionService {
|
||||
|
||||
@Autowired private BeanClass1 bean1;
|
||||
@Autowired private BeanClass2 bean2;
|
||||
@Autowired
|
||||
private BeanClass1 bean1;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("qual1")
|
||||
private BeanClass2 bean2;
|
||||
|
||||
public BeanClass1 getBean1() {
|
||||
return bean1;
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.test.injections;
|
||||
|
||||
import org.test.MainClass;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.DependsOn;
|
||||
import org.springframework.context.annotation.ImportRuntimeHints;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.test.BeanClass1;
|
||||
import org.test.BeanClass2;
|
||||
import org.test.ManuallyCreatedBeanWithConstructor;
|
||||
|
||||
@Configuration
|
||||
@Qualifier("qualifier")
|
||||
@ImportRuntimeHints(MainClass.class)
|
||||
public class ConfigurationWithInjectionsAndAnnotations {
|
||||
|
||||
@Bean
|
||||
@DependsOn({"bean1", "bean2"})
|
||||
ManuallyCreatedBeanWithConstructor beanWithAnnotationsOnInjectionPoints(@Qualifier("q1") BeanClass1 bean1, @Qualifier("q2") BeanClass2 bean2) {
|
||||
return new ManuallyCreatedBeanWithConstructor(bean1, bean2);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,9 +2,14 @@ package org.test.springdata;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
@Qualifier("repoQualifier")
|
||||
@Profile({"prof1", "prof2"})
|
||||
public interface CustomerRepository extends CrudRepository<Customer, Long> {
|
||||
|
||||
List<Customer> findByLastName(String lastName);
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user