Skip to content

Commit cba0086

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

File tree

3 files changed

+187
-58
lines changed

3 files changed

+187
-58
lines changed

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

Lines changed: 2 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import org.eclipse.jdt.core.dom.ExpressionStatement;
2323
import org.eclipse.jdt.core.dom.FieldAccess;
2424
import org.eclipse.jdt.core.dom.FieldDeclaration;
25-
import org.eclipse.jdt.core.dom.ImportDeclaration;
2625
import org.eclipse.jdt.core.dom.MethodDeclaration;
2726
import org.eclipse.jdt.core.dom.Modifier;
2827
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
@@ -167,11 +166,11 @@ public TextEdit computeEdit() throws Exception {
167166
}
168167
}
169168

170-
// Step 3: Add imports for all referenced class types (sorted for correct insertion order)
169+
// Step 3: Add imports for all referenced class types (sorted so insertions are in order)
171170
List<ClassName> classNames = new ArrayList<>(beanType.getAllClassNames());
172171
classNames.sort((a, b) -> a.getFullyQualifiedName().compareTo(b.getFullyQualifiedName()));
173172
for (ClassName cn : classNames) {
174-
addImport(rewrite, ast, cu, cn);
173+
JdtRefactorUtils.addImport(rewrite, ast, cu, cn);
175174
}
176175

177176
// Generate Eclipse TextEdit
@@ -351,59 +350,4 @@ private void addFieldAssignment(ASTRewrite rewrite, AST ast, MethodDeclaration c
351350
bodyRewrite.insertLast(assignStmt, null);
352351
}
353352

354-
// ========== Import ==========
355-
356-
private static void addImport(ASTRewrite rewrite, AST ast, CompilationUnit cu, ClassName className) {
357-
String packageName = className.getPackageName();
358-
359-
// Don't add import for java.lang types
360-
if ("java.lang".equals(packageName)) {
361-
return;
362-
}
363-
364-
// Don't add import for default package types
365-
if (packageName.isEmpty()) {
366-
return;
367-
}
368-
369-
// Don't add import if type is in the same package
370-
if (cu.getPackage() != null) {
371-
String cuPackage = cu.getPackage().getName().getFullyQualifiedName();
372-
if (cuPackage.equals(packageName)) {
373-
return;
374-
}
375-
}
376-
377-
String fullyQualifiedName = className.getFullyQualifiedName();
378-
379-
// Check if import already exists
380-
for (Object importObj : cu.imports()) {
381-
ImportDeclaration imp = (ImportDeclaration) importObj;
382-
if (imp.getName().getFullyQualifiedName().equals(fullyQualifiedName)) {
383-
return;
384-
}
385-
}
386-
387-
ImportDeclaration importDecl = ast.newImportDeclaration();
388-
importDecl.setName(ast.newName(fullyQualifiedName));
389-
390-
ListRewrite importsRewrite = rewrite.getListRewrite(cu, CompilationUnit.IMPORTS_PROPERTY);
391-
392-
// Insert in sorted order
393-
ImportDeclaration insertBefore = null;
394-
for (Object importObj : cu.imports()) {
395-
ImportDeclaration existing = (ImportDeclaration) importObj;
396-
if (existing.getName().getFullyQualifiedName().compareTo(fullyQualifiedName) > 0) {
397-
insertBefore = existing;
398-
break;
399-
}
400-
}
401-
402-
if (insertBefore != null) {
403-
importsRewrite.insertBefore(importDecl, insertBefore, null);
404-
} else {
405-
importsRewrite.insertLast(importDecl, null);
406-
}
407-
}
408-
409353
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2026 Broadcom, Inc.
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v1.0
5+
* which accompanies this distribution, and is available at
6+
* https://www.eclipse.org/legal/epl-v10.html
7+
*
8+
* Contributors:
9+
* Broadcom, Inc. - initial API and implementation
10+
*******************************************************************************/
11+
package org.springframework.ide.vscode.boot.java.jdt.refactoring;
12+
13+
import org.eclipse.jdt.core.dom.AST;
14+
import org.eclipse.jdt.core.dom.CompilationUnit;
15+
import org.eclipse.jdt.core.dom.ImportDeclaration;
16+
import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
17+
import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
18+
19+
/**
20+
* Shared utility methods for JDT-based refactorings.
21+
*
22+
* @author Alex Boyko
23+
*/
24+
public final class JdtRefactorUtils {
25+
26+
private JdtRefactorUtils() {
27+
// utility class
28+
}
29+
30+
/**
31+
* Add an import for the given {@link ClassName} to the compilation unit, unless
32+
* the import is unnecessary.
33+
* <p>
34+
* An import is considered unnecessary when any of the following is true:
35+
* <ul>
36+
* <li>The type is in the {@code java.lang} package</li>
37+
* <li>The type is in the default (unnamed) package</li>
38+
* <li>The type is in the same package as the compilation unit</li>
39+
* <li>An exact import for the type already exists</li>
40+
* <li>A wildcard (on-demand) import already covers the type's package</li>
41+
* </ul>
42+
* <p>
43+
* When an import is added, it is inserted in lexicographic sorted order among
44+
* the existing imports.
45+
*
46+
* @param rewrite the {@link ASTRewrite} to record the change
47+
* @param ast the AST factory
48+
* @param cu the compilation unit
49+
* @param className the class name to import
50+
*/
51+
public static void addImport(ASTRewrite rewrite, AST ast, CompilationUnit cu, ClassName className) {
52+
String packageName = className.getPackageName();
53+
54+
// Don't add import for java.lang types
55+
if ("java.lang".equals(packageName)) {
56+
return;
57+
}
58+
59+
// Don't add import for default package types
60+
if (packageName.isEmpty()) {
61+
return;
62+
}
63+
64+
// Don't add import if type is in the same package
65+
if (cu.getPackage() != null) {
66+
String cuPackage = cu.getPackage().getName().getFullyQualifiedName();
67+
if (cuPackage.equals(packageName)) {
68+
return;
69+
}
70+
}
71+
72+
String fullyQualifiedName = className.getFullyQualifiedName();
73+
74+
// Check if import already exists (exact or wildcard)
75+
for (Object importObj : cu.imports()) {
76+
ImportDeclaration imp = (ImportDeclaration) importObj;
77+
if (imp.isOnDemand()) {
78+
// Wildcard import like "import java.util.*"
79+
if (imp.getName().getFullyQualifiedName().equals(packageName)) {
80+
return;
81+
}
82+
} else if (imp.getName().getFullyQualifiedName().equals(fullyQualifiedName)) {
83+
return;
84+
}
85+
}
86+
87+
ImportDeclaration importDecl = ast.newImportDeclaration();
88+
importDecl.setName(ast.newName(fullyQualifiedName));
89+
90+
ListRewrite importsRewrite = rewrite.getListRewrite(cu, CompilationUnit.IMPORTS_PROPERTY);
91+
92+
// Insert in sorted order
93+
ImportDeclaration insertBefore = null;
94+
for (Object importObj : cu.imports()) {
95+
ImportDeclaration existing = (ImportDeclaration) importObj;
96+
if (existing.getName().getFullyQualifiedName().compareTo(fullyQualifiedName) > 0) {
97+
insertBefore = existing;
98+
break;
99+
}
100+
}
101+
102+
if (insertBefore != null) {
103+
importsRewrite.insertBefore(importDecl, insertBefore, null);
104+
} else {
105+
importsRewrite.insertLast(importDecl, null);
106+
}
107+
}
108+
109+
}

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

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,82 @@ public void doWork() {
318318
""", result);
319319
}
320320

321+
@Test
322+
void noImportWhenWildcardImportCoversPackage() throws Exception {
323+
String source = """
324+
package com.example;
325+
326+
import java.util.*;
327+
328+
public class MyService {
329+
330+
public void doWork() {
331+
}
332+
}
333+
""";
334+
335+
String result = applyRefactoring(source,
336+
"java.util.List<java.lang.String>", "names",
337+
"com.example.MyService", true);
338+
339+
assertEquals("""
340+
package com.example;
341+
342+
import java.util.*;
343+
344+
public class MyService {
345+
346+
private final List<String> names;
347+
348+
MyService(List<String> names) {
349+
this.names = names;
350+
}
351+
352+
public void doWork() {
353+
}
354+
}
355+
""", result);
356+
}
357+
358+
@Test
359+
void wildcardImportDoesNotCoverDifferentPackage() throws Exception {
360+
String source = """
361+
package com.example;
362+
363+
import java.util.*;
364+
365+
public class MyService {
366+
367+
public void doWork() {
368+
}
369+
}
370+
""";
371+
372+
String result = applyRefactoring(source,
373+
"java.util.concurrent.ConcurrentMap<java.lang.String, java.lang.Integer>",
374+
"cache",
375+
"com.example.MyService", true);
376+
377+
assertEquals("""
378+
package com.example;
379+
380+
import java.util.*;
381+
import java.util.concurrent.ConcurrentMap;
382+
383+
public class MyService {
384+
385+
private final ConcurrentMap<String, Integer> cache;
386+
387+
MyService(ConcurrentMap<String, Integer> cache) {
388+
this.cache = cache;
389+
}
390+
391+
public void doWork() {
392+
}
393+
}
394+
""", result);
395+
}
396+
321397
// ========== Inner classes ==========
322398

323399
@Test

0 commit comments

Comments
 (0)