Skip to content

Commit 58a21c2

Browse files
committed
WIP: polish
Signed-off-by: BoykoAlex <alex.boyko@broadcom.com>
1 parent be6ef0e commit 58a21c2

File tree

7 files changed

+112
-48
lines changed

7 files changed

+112
-48
lines changed

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

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
import java.util.function.Supplier;
1717

1818
import org.apache.commons.text.similarity.JaroWinklerSimilarity;
19-
import org.checkerframework.checker.signature.qual.FullyQualifiedName;
2019
import org.eclipse.jdt.core.JavaCore;
2120
import org.eclipse.jdt.core.dom.ASTNode;
2221
import org.eclipse.jdt.core.dom.Assignment;
@@ -32,11 +31,11 @@
3231
import org.eclipse.text.edits.InsertEdit;
3332
import org.eclipse.text.edits.ReplaceEdit;
3433
import org.eclipse.text.edits.TextEdit;
35-
import org.openrewrite.java.tree.JavaType;
3634
import org.slf4j.Logger;
3735
import org.slf4j.LoggerFactory;
3836
import org.springframework.ide.vscode.boot.java.handlers.BootJavaCompletionEngine;
3937
import org.springframework.ide.vscode.boot.java.jdt.refactoring.InjectBeanConstructorRefactoring;
38+
import org.springframework.ide.vscode.boot.java.jdt.refactoring.JavaType;
4039
import org.springframework.ide.vscode.commons.java.IJavaProject;
4140
import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits;
4241
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposalWithScore;
@@ -58,7 +57,7 @@ public class BeanCompletionProposal implements ICompletionProposalWithScore {
5857
private IDocument doc;
5958
private IJavaProject project;
6059
private String beanId;
61-
private String beanType;
60+
private JavaType beanType;
6261
private String fieldName;
6362
private String className;
6463
private double score;
@@ -75,7 +74,7 @@ public BeanCompletionProposal(ASTNode node, int offset, IDocument doc, IJavaProj
7574
this.doc = doc;
7675
this.project = project;
7776
this.beanId = beanId;
78-
this.beanType = beanType;
77+
this.beanType = JavaType.parse(beanType);
7978
this.fieldName = fieldName;
8079
this.className = className;
8180
this.prefix = computePrefix();
@@ -172,7 +171,7 @@ public String getDetail() {
172171
public CompletionItemLabelDetails getLabelDetails() {
173172
CompletionItemLabelDetails labelDetails = new CompletionItemLabelDetails();
174173
labelDetails.setDetail(SHORT_DESCRIPTION);
175-
// labelDetails.setDescription(((FullyQualifiedName) JavaType.parse(beanType)).getSimpleName());
174+
labelDetails.setDescription(beanType.getDisplayName());
176175
return labelDetails;
177176
}
178177

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/jdt/refactoring/ArrayTypeName.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
package org.springframework.ide.vscode.boot.java.jdt.refactoring;
1212

1313
import java.util.List;
14+
import java.util.Objects;
1415

1516
import org.eclipse.jdt.core.dom.AST;
1617
import org.eclipse.jdt.core.dom.Type;
@@ -73,4 +74,17 @@ public String toString() {
7374
return componentType + "[]".repeat(dimensions);
7475
}
7576

77+
@Override
78+
public boolean equals(Object o) {
79+
if (this == o) return true;
80+
if (o == null || getClass() != o.getClass()) return false;
81+
ArrayTypeName that = (ArrayTypeName) o;
82+
return dimensions == that.dimensions && componentType.equals(that.componentType);
83+
}
84+
85+
@Override
86+
public int hashCode() {
87+
return Objects.hash(componentType, dimensions);
88+
}
89+
7690
}

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/jdt/refactoring/ClassName.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
package org.springframework.ide.vscode.boot.java.jdt.refactoring;
1212

1313
import java.util.List;
14+
import java.util.Objects;
1415

1516
import org.eclipse.jdt.core.dom.AST;
1617
import org.eclipse.jdt.core.dom.Type;
@@ -133,12 +134,14 @@ public boolean equals(Object o) {
133134
if (this == o) return true;
134135
if (o == null || getClass() != o.getClass()) return false;
135136
ClassName that = (ClassName) o;
136-
return getFullyQualifiedName().equals(that.getFullyQualifiedName());
137+
return simpleName.equals(that.simpleName)
138+
&& packageName.equals(that.packageName)
139+
&& Objects.equals(declaringClass, that.declaringClass);
137140
}
138141

139142
@Override
140143
public int hashCode() {
141-
return getFullyQualifiedName().hashCode();
144+
return Objects.hash(packageName, simpleName, declaringClass);
142145
}
143146

144147
}

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/jdt/refactoring/InjectBeanConstructorRefactoring.java

Lines changed: 60 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
import java.util.List;
1515
import java.util.Map;
1616

17-
import org.eclipse.jdt.core.JavaCore;
1817
import org.eclipse.jdt.core.dom.AST;
1918
import org.eclipse.jdt.core.dom.ASTNode;
2019
import org.eclipse.jdt.core.dom.Assignment;
@@ -67,52 +66,74 @@ public class InjectBeanConstructorRefactoring {
6766

6867
private final CompilationUnit cu;
6968
private final String source;
70-
private final String fullyQualifiedBeanType;
69+
private final JavaType beanType;
7170
private final String fieldName;
7271
private final String targetClassFqName;
7372
private final boolean addFieldAssignment;
7473
private final Map<String, String> formatterOptions;
7574

7675
/**
77-
* Create a new bean constructor injection refactoring.
76+
* Create a new bean constructor injection refactoring from a pre-parsed {@link JavaType}.
77+
* <p>
78+
* Use this constructor when the caller already has a parsed {@link JavaType} instance
79+
* to avoid redundant parsing.
7880
*
79-
* @param cu the already-parsed {@link CompilationUnit}. The caller provides
80-
* it — in the completion flow it comes from the CU cache; in tests
81-
* it can be a simple {@code ASTParser}-created CU.
82-
* @param source the full Java source text of the compilation unit. Required because
83-
* the DOM {@link CompilationUnit} does not retain the original source
84-
* text, and {@link ASTRewrite#rewriteAST(org.eclipse.jface.text.IDocument, Map)}
85-
* needs it to compute correct text edits.
86-
* @param fullyQualifiedBeanType the fully qualified type name of the bean to inject
87-
* (e.g. {@code "org.springframework.samples.petclinic.owner.OwnerRepository"})
81+
* @param cu the already-parsed {@link CompilationUnit}
82+
* @param source the full Java source text of the compilation unit
83+
* @param beanType the parsed type of the bean to inject
8884
* @param fieldName the desired field name for the injected bean
8985
* @param targetClassFqName the fully qualified name of the class to inject the bean into
9086
* @param addFieldAssignment whether to add the {@code this.field = field} assignment
91-
* in the constructor body. Pass {@code false} when the caller
92-
* handles the assignment separately (e.g. as the primary text edit
93-
* in a completion proposal when the cursor is inside a constructor)
87+
* in the constructor body
9488
* @param formatterOptions JavaCore formatter options controlling indentation, tab/space
95-
* settings, etc. These are passed directly to
96-
* {@link ASTRewrite#rewriteAST(org.eclipse.jface.text.IDocument, Map)}.
97-
* A sensible default can be obtained via {@link JavaCore#getOptions()}.
89+
* settings, etc.
9890
*/
9991
public InjectBeanConstructorRefactoring(
10092
CompilationUnit cu,
10193
String source,
102-
String fullyQualifiedBeanType,
94+
JavaType beanType,
10395
String fieldName,
10496
String targetClassFqName,
10597
boolean addFieldAssignment,
10698
Map<String, String> formatterOptions) {
10799
this.cu = cu;
108100
this.source = source;
109-
this.fullyQualifiedBeanType = fullyQualifiedBeanType;
101+
this.beanType = beanType;
110102
this.fieldName = fieldName;
111103
this.targetClassFqName = targetClassFqName;
112104
this.addFieldAssignment = addFieldAssignment;
113105
this.formatterOptions = formatterOptions;
114106
}
115107

108+
/**
109+
* Create a new bean constructor injection refactoring from a type name string.
110+
* <p>
111+
* Convenience constructor that parses the type string into a {@link JavaType}.
112+
* Use {@code $} for inner classes (e.g. {@code "java.util.Map$Entry"}).
113+
*
114+
* @param cu the already-parsed {@link CompilationUnit}
115+
* @param source the full Java source text of the compilation unit
116+
* @param fullyQualifiedBeanType the fully qualified type name of the bean to inject
117+
* (e.g. {@code "org.springframework.samples.petclinic.owner.OwnerRepository"})
118+
* @param fieldName the desired field name for the injected bean
119+
* @param targetClassFqName the fully qualified name of the class to inject the bean into
120+
* @param addFieldAssignment whether to add the {@code this.field = field} assignment
121+
* in the constructor body
122+
* @param formatterOptions JavaCore formatter options controlling indentation, tab/space
123+
* settings, etc.
124+
*/
125+
public InjectBeanConstructorRefactoring(
126+
CompilationUnit cu,
127+
String source,
128+
String fullyQualifiedBeanType,
129+
String fieldName,
130+
String targetClassFqName,
131+
boolean addFieldAssignment,
132+
Map<String, String> formatterOptions) {
133+
this(cu, source, JavaType.parse(fullyQualifiedBeanType), fieldName, targetClassFqName,
134+
addFieldAssignment, formatterOptions);
135+
}
136+
116137
/**
117138
* Compute the JDT {@link TextEdit} representing all source modifications.
118139
*
@@ -129,35 +150,28 @@ public TextEdit computeEdit() throws Exception {
129150
return null;
130151
}
131152

132-
// Parse the (possibly parameterized) type string using the type model
133-
JavaType javaType = JavaType.parse(fullyQualifiedBeanType);
134-
135153
// Step 1: Add private final field
136-
Type fieldType = javaType.toType(ast);
154+
Type fieldType = beanType.toType(ast);
137155
FieldDeclaration newField = addField(rewrite, ast, targetClass, fieldType);
138156

139157
// Step 2: Find or create constructor and add parameter + assignment
140158
MethodDeclaration constructor = findConstructor(targetClass);
141159
if (constructor == null) {
142-
Type ctorParamType = javaType.toType(ast);
160+
Type ctorParamType = beanType.toType(ast);
143161
createConstructor(rewrite, ast, targetClass, ctorParamType, newField);
144162
} else {
145-
Type ctorParamType = javaType.toType(ast);
163+
Type ctorParamType = beanType.toType(ast);
146164
addParameterToConstructor(rewrite, ast, constructor, ctorParamType);
147165
if (addFieldAssignment) {
148166
addFieldAssignment(rewrite, ast, constructor);
149167
}
150168
}
151169

152170
// Step 3: Add imports for all referenced class types (sorted for correct insertion order)
153-
List<ClassName> classNames = javaType.getAllClassNames();
154-
List<String> sortedImports = new ArrayList<>();
171+
List<ClassName> classNames = new ArrayList<>(beanType.getAllClassNames());
172+
classNames.sort((a, b) -> a.getFullyQualifiedName().compareTo(b.getFullyQualifiedName()));
155173
for (ClassName cn : classNames) {
156-
sortedImports.add(cn.getFullyQualifiedName());
157-
}
158-
sortedImports.sort(String::compareTo);
159-
for (String fqName : sortedImports) {
160-
addImport(rewrite, ast, cu, fqName);
174+
addImport(rewrite, ast, cu, cn);
161175
}
162176

163177
// Generate Eclipse TextEdit
@@ -339,23 +353,29 @@ private void addFieldAssignment(ASTRewrite rewrite, AST ast, MethodDeclaration c
339353

340354
// ========== Import ==========
341355

342-
private static void addImport(ASTRewrite rewrite, AST ast, CompilationUnit cu, String fullyQualifiedName) {
356+
private static void addImport(ASTRewrite rewrite, AST ast, CompilationUnit cu, ClassName className) {
357+
String packageName = className.getPackageName();
358+
343359
// Don't add import for java.lang types
344-
if (fullyQualifiedName.startsWith("java.lang.") && fullyQualifiedName.indexOf('.', 10) == -1) {
360+
if ("java.lang".equals(packageName)) {
361+
return;
362+
}
363+
364+
// Don't add import for default package types
365+
if (packageName.isEmpty()) {
345366
return;
346367
}
347368

348369
// Don't add import if type is in the same package
349370
if (cu.getPackage() != null) {
350-
String packageName = cu.getPackage().getName().getFullyQualifiedName();
351-
String typePackage = fullyQualifiedName.contains(".")
352-
? fullyQualifiedName.substring(0, fullyQualifiedName.lastIndexOf('.'))
353-
: "";
354-
if (packageName.equals(typePackage)) {
371+
String cuPackage = cu.getPackage().getName().getFullyQualifiedName();
372+
if (cuPackage.equals(packageName)) {
355373
return;
356374
}
357375
}
358376

377+
String fullyQualifiedName = className.getFullyQualifiedName();
378+
359379
// Check if import already exists
360380
for (Object importObj : cu.imports()) {
361381
ImportDeclaration imp = (ImportDeclaration) importObj;

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/jdt/refactoring/JavaType.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
*
3737
* @author Alex Boyko
3838
*/
39-
interface JavaType {
39+
public interface JavaType {
4040

4141
/**
4242
* Returns a human-readable display name for this type using short (simple) names.

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/jdt/refactoring/ParameterizedClassName.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import java.util.ArrayList;
1414
import java.util.Collections;
1515
import java.util.List;
16+
import java.util.Objects;
1617
import java.util.stream.Collectors;
1718

1819
import org.eclipse.jdt.core.dom.AST;
@@ -115,4 +116,17 @@ public String toString() {
115116
return getFullyQualifiedName();
116117
}
117118

119+
@Override
120+
public boolean equals(Object o) {
121+
if (this == o) return true;
122+
if (o == null || getClass() != o.getClass()) return false;
123+
ParameterizedClassName that = (ParameterizedClassName) o;
124+
return erasure.equals(that.erasure) && typeArguments.equals(that.typeArguments);
125+
}
126+
127+
@Override
128+
public int hashCode() {
129+
return Objects.hash(erasure, typeArguments);
130+
}
131+
118132
}

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/jdt/refactoring/WildcardName.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import java.util.Collections;
1414
import java.util.List;
15+
import java.util.Objects;
1516

1617
import org.eclipse.jdt.core.dom.AST;
1718
import org.eclipse.jdt.core.dom.Type;
@@ -89,4 +90,17 @@ public String toString() {
8990
return "? " + (upperBound ? "extends" : "super") + " " + bound;
9091
}
9192

93+
@Override
94+
public boolean equals(Object o) {
95+
if (this == o) return true;
96+
if (o == null || getClass() != o.getClass()) return false;
97+
WildcardName that = (WildcardName) o;
98+
return upperBound == that.upperBound && Objects.equals(bound, that.bound);
99+
}
100+
101+
@Override
102+
public int hashCode() {
103+
return Objects.hash(bound, upperBound);
104+
}
105+
92106
}

0 commit comments

Comments
 (0)