Skip to content

Commit 88c3287

Browse files
committed
GH-1375: added check to not show bean names as qualifier proposals on bean methods
1 parent 3b95268 commit 88c3287

File tree

2 files changed

+115
-26
lines changed

2 files changed

+115
-26
lines changed

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

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,14 @@
1212

1313
import java.util.Arrays;
1414
import java.util.LinkedHashMap;
15+
import java.util.List;
1516
import java.util.Map;
1617
import java.util.stream.Collectors;
1718
import java.util.stream.Stream;
1819

1920
import org.eclipse.jdt.core.dom.ASTNode;
21+
import org.eclipse.jdt.core.dom.Annotation;
22+
import org.eclipse.jdt.core.dom.IAnnotationBinding;
2023
import org.eclipse.jdt.core.dom.MethodDeclaration;
2124
import org.eclipse.jdt.core.dom.TypeDeclaration;
2225
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
@@ -42,9 +45,10 @@ public Map<String, String> getCompletionCandidates(IJavaProject project, ASTNode
4245
Bean[] beans = this.springIndex.getBeansOfProject(project.getElementName());
4346

4447
boolean isOnType = isAnnotationOnType(node);
48+
boolean isOnBeanMethod = isOnBeanMethod(node);
4549

4650
Stream<String> candidates = findAllQualifiers(beans);
47-
if (!isOnType) {
51+
if (!isOnType && !isOnBeanMethod) {
4852
candidates = Stream.concat(candidates, Arrays.stream(beans).map(bean -> bean.getName()));
4953
}
5054

@@ -89,5 +93,44 @@ else if (node instanceof TypeDeclaration) {
8993

9094
return false;
9195
}
96+
97+
private boolean isOnBeanMethod(ASTNode node) {
98+
Annotation annotation = findAnnotation(node);
99+
if (annotation != null) {
100+
if (annotation.getParent() instanceof MethodDeclaration) {
101+
MethodDeclaration method = (MethodDeclaration) annotation.getParent();
102+
return hasBeanAnnotation(method);
103+
}
104+
}
105+
return false;
106+
}
107+
108+
private boolean hasBeanAnnotation(MethodDeclaration method) {
109+
List<?> modifiers = method.modifiers();
110+
for (Object modifier : modifiers) {
111+
if (modifier instanceof Annotation) {
112+
Annotation annotation = (Annotation) modifier;
113+
IAnnotationBinding annotationBinding = annotation.resolveAnnotationBinding();
114+
String type = annotationBinding.getAnnotationType().getBinaryName();
115+
116+
if (type != null && Annotations.BEAN.equals(type)) {
117+
return true;
118+
}
119+
}
120+
}
121+
122+
return false;
123+
}
124+
125+
private Annotation findAnnotation(ASTNode node) {
126+
while (node != null) {
127+
if (node instanceof Annotation) {
128+
return (Annotation) node;
129+
}
130+
node = node.getParent();
131+
}
132+
133+
return null;
134+
}
92135

93136
}

headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/beans/test/QualifierCompletionProviderTest.java

Lines changed: 71 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -96,86 +96,106 @@ public void restoreIndexState() {
9696

9797
@Test
9898
public void testQualifierCompletionWithoutQuotesWithoutPrefixAtInjecitonPoint() throws Exception {
99-
assertCompletions("@Qualifier(<*>)", new String[] {"quali1", "quali2", "bean1", "bean2"}, 0, "@Qualifier(\"quali1\"<*>)", false);
99+
assertCompletions("@Qualifier(<*>)", new String[] {"quali1", "quali2", "bean1", "bean2"}, 0, "@Qualifier(\"quali1\"<*>)", PositionInCode.ON_INJECTION_POINT);
100100
}
101101

102102
@Test
103103
public void testQualifierCompletionWithoutQuotesWithoutPrefixAtTypeDeclaration() throws Exception {
104-
assertCompletions("@Qualifier(<*>)", new String[] {"quali1", "quali2"}, 0, "@Qualifier(\"quali1\"<*>)", true);
104+
assertCompletions("@Qualifier(<*>)", new String[] {"quali1", "quali2"}, 0, "@Qualifier(\"quali1\"<*>)", PositionInCode.ON_TYPE);
105+
}
106+
107+
@Test
108+
public void testQualifierCompletionWithoutQuotesWithoutPrefixAtBeanMethod() throws Exception {
109+
assertCompletions("@Qualifier(<*>)", new String[] {"quali1", "quali2"}, 0, "@Qualifier(\"quali1\"<*>)", PositionInCode.ON_BEAN_METHOD);
105110
}
106111

107112
@Test
108113
public void testQualifierCompletionWithoutQuotesWithPrefixAtInjecitonPoint() throws Exception {
109-
assertCompletions("@Qualifier(be<*>)", 2, "@Qualifier(\"bean1\"<*>)", false);
114+
assertCompletions("@Qualifier(be<*>)", 2, "@Qualifier(\"bean1\"<*>)", PositionInCode.ON_INJECTION_POINT);
115+
}
116+
117+
@Test
118+
public void testQualifierCompletionWithoutQuotesWithPrefixAtTypeDeclaration() throws Exception {
119+
assertCompletions("@Qualifier(be<*>)", 0, null, PositionInCode.ON_TYPE);
120+
}
121+
122+
@Test
123+
public void testQualifierCompletionWithoutQuotesWithPrefixAtBeanMethod() throws Exception {
124+
assertCompletions("@Qualifier(be<*>)", 0, null, PositionInCode.ON_BEAN_METHOD);
110125
}
111126

112127
@Test
113128
public void testQualifierCompletionWithoutQuotesWithPrefixFromExistingQualifierAtInjecitonPoint() throws Exception {
114-
assertCompletions("@Qualifier(qu<*>)", new String[] {"quali1", "quali2"}, 0, "@Qualifier(\"quali1\"<*>)", false);
129+
assertCompletions("@Qualifier(qu<*>)", new String[] {"quali1", "quali2"}, 0, "@Qualifier(\"quali1\"<*>)", PositionInCode.ON_INJECTION_POINT);
115130
}
116131

117132
@Test
118133
@Disabled // TODO: implement this case
119134
public void testQualifierCompletionWithoutQuotesWithAttributeNameAtInjecitonPoint() throws Exception {
120-
assertCompletions("@Qualifier(value=<*>)", 4, "@Qualifier(value=\"quali1\"<*>)", false);
135+
assertCompletions("@Qualifier(value=<*>)", 4, "@Qualifier(value=\"quali1\"<*>)", PositionInCode.ON_INJECTION_POINT);
121136
}
122137

123138
@Test
124139
public void testQualifierCompletionWithoutQuotesWithAttributeNameAtTypeDeclaration() throws Exception {
125-
assertCompletions("@Qualifier(value=<*>)", 2, "@Qualifier(value=\"quali1\"<*>)", true);
140+
assertCompletions("@Qualifier(value=<*>)", 2, "@Qualifier(value=\"quali1\"<*>)", PositionInCode.ON_TYPE);
141+
}
142+
143+
@Test
144+
public void testQualifierCompletionWithoutQuotesWithAttributeNameAtBeanMethod() throws Exception {
145+
assertCompletions("@Qualifier(value=<*>)", 2, "@Qualifier(value=\"quali1\"<*>)", PositionInCode.ON_BEAN_METHOD);
126146
}
127147

128148
@Test
129149
@Disabled // TODO: implement this case
130150
public void testQualifierCompletionWithoutQuotesWithAttributeNameAndSpacesAtInjecitonPoint() throws Exception {
131-
assertCompletions("@Qualifier(value = <*>)", 4, "@Qualifier(value = \"quali1\"<*>)", false);
151+
assertCompletions("@Qualifier(value = <*>)", 4, "@Qualifier(value = \"quali1\"<*>)", PositionInCode.ON_INJECTION_POINT);
132152
}
133153

134154
@Test
135155
public void testQualifierCompletionWithoutQuotesWithAttributeNameAndSpacesAtTypeDeclaration() throws Exception {
136-
assertCompletions("@Qualifier(value = <*>)", 2, "@Qualifier(value = \"quali1\"<*>)", true);
156+
assertCompletions("@Qualifier(value = <*>)", 2, "@Qualifier(value = \"quali1\"<*>)", PositionInCode.ON_TYPE);
137157
}
138158

139159
@Test
140160
public void testQualifierCompletionInsideOfQuotesWithoutPrefixAtInjecitonPoint() throws Exception {
141-
assertCompletions("@Qualifier(\"<*>\")", 4, "@Qualifier(\"quali1<*>\")", false);
161+
assertCompletions("@Qualifier(\"<*>\")", 4, "@Qualifier(\"quali1<*>\")", PositionInCode.ON_INJECTION_POINT);
142162
}
143163

144164
@Test
145165
public void testQualifierCompletionInsideOfQuotesWithPrefixAtInjecitonPoint() throws Exception {
146-
assertCompletions("@Qualifier(\"be<*>\")", 2, "@Qualifier(\"bean1<*>\")", false);
166+
assertCompletions("@Qualifier(\"be<*>\")", 2, "@Qualifier(\"bean1<*>\")", PositionInCode.ON_INJECTION_POINT);
147167
}
148168

149169
@Test
150170
public void testQualifierCompletionInsideOfQuotesWithPrefixButWithoutMatchesAtInjecitonPoint() throws Exception {
151-
assertCompletions("@Qualifier(\"XXX<*>\")", 0, null, false);
171+
assertCompletions("@Qualifier(\"XXX<*>\")", 0, null, PositionInCode.ON_INJECTION_POINT);
152172
}
153173

154174
@Test
155175
public void testQualifierCompletionOutsideOfAnnotation1AtInjecitonPoint() throws Exception {
156-
assertCompletions("@Qualifier(\"XXX\")<*>", 0, null, false);
176+
assertCompletions("@Qualifier(\"XXX\")<*>", 0, null, PositionInCode.ON_INJECTION_POINT);
157177
}
158178

159179
@Test
160180
public void testQualifierCompletionOutsideOfAnnotation2AtInjecitonPoint() throws Exception {
161-
assertCompletions("@Qualifier<*>(\"XXX\")", 0, null, false);
181+
assertCompletions("@Qualifier<*>(\"XXX\")", 0, null, PositionInCode.ON_INJECTION_POINT);
162182
}
163183

164184
@Test
165185
public void testQualifierCompletionInsideOfQuotesWithPrefixAndReplacedPostfixAtInjecitonPoint() throws Exception {
166-
assertCompletions("@Qualifier(\"be<*>xxx\")", 2, "@Qualifier(\"bean1<*>\")", false);
186+
assertCompletions("@Qualifier(\"be<*>xxx\")", 2, "@Qualifier(\"bean1<*>\")", PositionInCode.ON_INJECTION_POINT);
167187
}
168188

169-
private void assertCompletions(String completionLine, int noOfExpectedCompletions, String expectedCompletedLine, boolean onType) throws Exception {
170-
assertCompletions(completionLine, noOfExpectedCompletions, null, 0, expectedCompletedLine, onType);
189+
private void assertCompletions(String completionLine, int noOfExpectedCompletions, String expectedCompletedLine, PositionInCode position) throws Exception {
190+
assertCompletions(completionLine, noOfExpectedCompletions, null, 0, expectedCompletedLine, position);
171191
}
172192

173-
private void assertCompletions(String completionLine, String[] expectedCompletions, int chosenCompletion, String expectedCompletedLine, boolean onType) throws Exception {
174-
assertCompletions(completionLine, expectedCompletions.length, expectedCompletions, chosenCompletion, expectedCompletedLine, onType);
193+
private void assertCompletions(String completionLine, String[] expectedCompletions, int chosenCompletion, String expectedCompletedLine, PositionInCode position) throws Exception {
194+
assertCompletions(completionLine, expectedCompletions.length, expectedCompletions, chosenCompletion, expectedCompletedLine, position);
175195
}
176196

177-
private void assertCompletions(String completionLine, int noOfExcpectedCompletions, String[] expectedCompletions, int chosenCompletion, String expectedCompletedLine, boolean onType) throws Exception {
178-
String editorContent = createEditorContent(completionLine, onType);
197+
private void assertCompletions(String completionLine, int noOfExcpectedCompletions, String[] expectedCompletions, int chosenCompletion, String expectedCompletedLine, PositionInCode position) throws Exception {
198+
String editorContent = createEditorContent(completionLine, position);
179199
Editor editor = harness.newEditor(LanguageId.JAVA, editorContent, tempJavaDocUri);
180200

181201
List<CompletionItem> completions = editor.getCompletions();
@@ -191,12 +211,13 @@ private void assertCompletions(String completionLine, int noOfExcpectedCompletio
191211

192212
if (noOfExcpectedCompletions > 0) {
193213
editor.apply(completions.get(chosenCompletion));
194-
assertEquals(createEditorContent(expectedCompletedLine, onType), editor.getText());
214+
assertEquals(createEditorContent(expectedCompletedLine, position), editor.getText());
195215
}
196216
}
197217

198-
private String createEditorContent(String placeholderValue, boolean onType) {
199-
if (onType) {
218+
private String createEditorContent(String placeholderValue, PositionInCode position) {
219+
switch (position) {
220+
case ON_TYPE:
200221
return """
201222
package org.test;
202223
@@ -210,8 +231,7 @@ private String createEditorContent(String placeholderValue, boolean onType) {
210231
public class TestDependsOnClass {
211232
}
212233
""";
213-
}
214-
else {
234+
case ON_INJECTION_POINT:
215235
return """
216236
package org.test;
217237
@@ -227,8 +247,34 @@ public TestDependsOnClass(""" + placeholderValue + " Object someBean) {" +
227247
228248
}
229249
""";
250+
case ON_BEAN_METHOD:
251+
return """
252+
package org.test;
253+
254+
import org.springframework.context.annotation.Bean;
255+
import org.springframework.context.annotation.Configuration;
256+
import org.springframework.beans.factory.annotation.Qualifier;
257+
258+
@Configuration
259+
public class TestDependsOnClass {
260+
261+
@Bean
262+
""" +
263+
placeholderValue + "\n" +
264+
"""
265+
public Object myBean() {
266+
return new Object();
267+
}
268+
269+
}
270+
""";
271+
default: return null;
230272
}
231273
}
274+
275+
private static enum PositionInCode {
276+
ON_TYPE, ON_BEAN_METHOD, ON_INJECTION_POINT
277+
}
232278

233279

234280
}

0 commit comments

Comments
 (0)