Skip to content

Commit c45b52f

Browse files
Anmol202005rdiachenko
authored andcommitted
Issue: checkstyle#48: Created new Recipe for Redundant Import
1 parent 6281305 commit c45b52f

File tree

8 files changed

+293
-2
lines changed

8 files changed

+293
-2
lines changed

src/main/java/org/checkstyle/autofix/CheckstyleCheck.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@
2121
import java.util.Optional;
2222

2323
public enum CheckstyleCheck {
24-
UPPERELL("com.puppycrawl.tools.checkstyle.checks.UpperEllCheck"),
24+
FINALLOCALVARIABLE("com.puppycrawl.tools.checkstyle.checks.coding.FinalLocalVariableCheck"),
2525
HEADER("com.puppycrawl.tools.checkstyle.checks.header.HeaderCheck"),
26-
FINALLOCALVARIABLE("com.puppycrawl.tools.checkstyle.checks.coding.FinalLocalVariableCheck");
26+
REDUNDANTIMPORT("com.puppycrawl.tools.checkstyle.checks.imports.RedundantImportCheck"),
27+
UPPERELL("com.puppycrawl.tools.checkstyle.checks.UpperEllCheck");
2728

2829
private final String id;
2930

src/main/java/org/checkstyle/autofix/CheckstyleRecipeRegistry.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.checkstyle.autofix.parser.CheckstyleViolation;
3131
import org.checkstyle.autofix.recipe.FinalLocalVariable;
3232
import org.checkstyle.autofix.recipe.Header;
33+
import org.checkstyle.autofix.recipe.RedundantImport;
3334
import org.checkstyle.autofix.recipe.UpperEll;
3435
import org.openrewrite.Recipe;
3536

@@ -45,6 +46,7 @@ public final class CheckstyleRecipeRegistry {
4546
static {
4647
RECIPE_MAP.put(CheckstyleCheck.UPPERELL, UpperEll::new);
4748
RECIPE_MAP.put(CheckstyleCheck.FINALLOCALVARIABLE, FinalLocalVariable::new);
49+
RECIPE_MAP.put(CheckstyleCheck.REDUNDANTIMPORT, RedundantImport::new);
4850
RECIPE_MAP_WITH_CONFIG.put(CheckstyleCheck.HEADER, Header::new);
4951
}
5052

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
///////////////////////////////////////////////////////////////////////////////////////////////
2+
// checkstyle-openrewrite-recipes: Automatically fix Checkstyle violations with OpenRewrite.
3+
// Copyright (C) 2025 The Checkstyle OpenRewrite Recipes Authors
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
///////////////////////////////////////////////////////////////////////////////////////////////
17+
18+
package org.checkstyle.autofix.recipe;
19+
20+
import java.nio.file.Path;
21+
import java.util.HashSet;
22+
import java.util.List;
23+
import java.util.Set;
24+
25+
import org.checkstyle.autofix.PositionHelper;
26+
import org.checkstyle.autofix.parser.CheckstyleViolation;
27+
import org.openrewrite.ExecutionContext;
28+
import org.openrewrite.Recipe;
29+
import org.openrewrite.TreeVisitor;
30+
import org.openrewrite.java.JavaIsoVisitor;
31+
import org.openrewrite.java.tree.J;
32+
33+
public class RedundantImport extends Recipe {
34+
35+
private static final String JAVA_LANG_PREFIX = "java.lang.";
36+
37+
private final List<CheckstyleViolation> violations;
38+
39+
public RedundantImport(List<CheckstyleViolation> violations) {
40+
this.violations = violations;
41+
}
42+
43+
@Override
44+
public String getDisplayName() {
45+
return "Remove redundant imports";
46+
}
47+
48+
@Override
49+
public String getDescription() {
50+
return "Remove duplicate imports, java.lang imports, and same package imports";
51+
}
52+
53+
@Override
54+
public TreeVisitor<?, ExecutionContext> getVisitor() {
55+
return new RemoveRedundantImportsVisitor();
56+
}
57+
58+
private final class RemoveRedundantImportsVisitor extends JavaIsoVisitor<ExecutionContext> {
59+
60+
private Path sourcePath;
61+
62+
@Override
63+
public J.CompilationUnit visitCompilationUnit(J.CompilationUnit cu, ExecutionContext ctx) {
64+
65+
this.sourcePath = cu.getSourcePath().toAbsolutePath();
66+
67+
final Set<String> seenImports = new HashSet<>();
68+
final String currentPackage = getCurrentPackage(cu);
69+
70+
return cu.withImports(
71+
cu.getImports().stream()
72+
.filter(importStmt -> {
73+
return !isRedundant(importStmt, seenImports,
74+
currentPackage) || !isAtViolationLocation(importStmt); })
75+
.toList()
76+
);
77+
}
78+
79+
private boolean isRedundant(J.Import importStmt,
80+
Set<String> seenImports, String currentPackage) {
81+
final String importName = importStmt.getQualid().toString();
82+
83+
boolean isRedundant = false;
84+
85+
if (seenImports.contains(importName)) {
86+
isRedundant = true;
87+
}
88+
else {
89+
seenImports.add(importName);
90+
91+
if (!importStmt.isStatic() && importName.startsWith(JAVA_LANG_PREFIX)) {
92+
isRedundant = true;
93+
}
94+
else if (currentPackage != null && !importStmt.isStatic()
95+
&& importName.startsWith(currentPackage + ".")) {
96+
isRedundant = true;
97+
}
98+
}
99+
100+
return isRedundant;
101+
}
102+
103+
private String getCurrentPackage(J.CompilationUnit compilationUnit) {
104+
String currentPackage = null;
105+
if (compilationUnit.getPackageDeclaration() != null) {
106+
currentPackage = compilationUnit.getPackageDeclaration()
107+
.getExpression().printTrimmed(getCursor());
108+
}
109+
return currentPackage;
110+
}
111+
112+
private boolean isAtViolationLocation(J.Import literal) {
113+
final J.CompilationUnit cursor = getCursor().firstEnclosing(J.CompilationUnit.class);
114+
115+
final int line = PositionHelper.computeLinePosition(cursor, literal, getCursor());
116+
final int column = PositionHelper.computeColumnPosition(cursor, literal, getCursor());
117+
return violations.removeIf(violation -> {
118+
final Path absolutePath = Path.of(violation.getFileName()).toAbsolutePath();
119+
return violation.getLine() == line
120+
&& violation.getColumn() == column
121+
&& absolutePath.equals(sourcePath);
122+
});
123+
}
124+
125+
}
126+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
///////////////////////////////////////////////////////////////////////////////////////////////
2+
// checkstyle-openrewrite-recipes: Automatically fix Checkstyle violations with OpenRewrite.
3+
// Copyright (C) 2025 The Checkstyle OpenRewrite Recipes Authors
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
///////////////////////////////////////////////////////////////////////////////////////////////
17+
18+
package org.checkstyle.autofix.recipe;
19+
20+
import org.junit.jupiter.api.Test;
21+
22+
public class RedundantImportTest extends AbstractRecipeTestSupport {
23+
24+
@Override
25+
protected String getSubpackage() {
26+
return "redundantimport";
27+
}
28+
29+
@Test
30+
void duplicateImport() throws Exception {
31+
verify("DuplicateImport");
32+
}
33+
34+
@Test
35+
void complexCase() throws Exception {
36+
verify("ComplexCase");
37+
}
38+
39+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*xml
2+
<module name="Checker">
3+
<module name="TreeWalker">
4+
<module name="com.puppycrawl.tools.checkstyle.checks.imports.RedundantImportCheck"/>
5+
</module>
6+
</module>
7+
8+
*/
9+
10+
package org.checkstyle.autofix.recipe.redundantimport.complexcase;
11+
12+
import org.checkstyle.autofix.recipe.redundantimport.complexcase.*; // violation 'Redundant import from the same package'
13+
import java.io.*;
14+
import java.lang.*; // violation 'Redundant import from the java.lang package'
15+
16+
import java.util.List;
17+
import java.util.List; // violation 'Duplicate import'
18+
import java.util.Iterator;
19+
import java.util.Enumeration;
20+
import java.util.Arrays;
21+
22+
import javax.swing.JToolBar;
23+
import javax.swing.JToggleButton;
24+
import javax.swing.ScrollPaneLayout;
25+
import javax.swing.BorderFactory;
26+
27+
import static java.io.File.listRoots;
28+
import static javax.swing.WindowConstants.*;
29+
import static javax.swing.WindowConstants.*; // violation 'Duplicate import'
30+
import static java.io.File.createTempFile;
31+
import static java.io.File.pathSeparator;
32+
import static org.checkstyle.autofix.recipe.redundantimport.complexcase.InputComplexCase.myStaticMethod;
33+
34+
import java.awt.Component;
35+
import java.awt.Graphics2D;
36+
import java.awt.HeadlessException;
37+
import java.awt.Label;
38+
import java.util.Date;
39+
import java.util.Calendar;
40+
import java.util.BitSet;
41+
42+
import static java.lang.Math.PI;
43+
44+
class InputComplexCase {
45+
public static void myStaticMethod() {}
46+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*xml
2+
<module name="Checker">
3+
<module name="TreeWalker">
4+
<module name="com.puppycrawl.tools.checkstyle.checks.imports.RedundantImportCheck"/>
5+
</module>
6+
</module>
7+
8+
*/
9+
10+
package org.checkstyle.autofix.recipe.redundantimport.complexcase;
11+
import java.io.*;
12+
13+
import java.util.List;
14+
import java.util.Iterator;
15+
import java.util.Enumeration;
16+
import java.util.Arrays;
17+
18+
import javax.swing.JToolBar;
19+
import javax.swing.JToggleButton;
20+
import javax.swing.ScrollPaneLayout;
21+
import javax.swing.BorderFactory;
22+
23+
import static java.io.File.listRoots;
24+
import static javax.swing.WindowConstants.*;
25+
import static java.io.File.createTempFile;
26+
import static java.io.File.pathSeparator;
27+
import static org.checkstyle.autofix.recipe.redundantimport.complexcase.InputComplexCase.myStaticMethod;
28+
29+
import java.awt.Component;
30+
import java.awt.Graphics2D;
31+
import java.awt.HeadlessException;
32+
import java.awt.Label;
33+
import java.util.Date;
34+
import java.util.Calendar;
35+
import java.util.BitSet;
36+
37+
import static java.lang.Math.PI;
38+
39+
class OutputComplexCase {
40+
public static void myStaticMethod() {}
41+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*xml
2+
<module name="Checker">
3+
<module name="TreeWalker">
4+
<module name="com.puppycrawl.tools.checkstyle.checks.imports.RedundantImportCheck"/>
5+
</module>
6+
</module>
7+
8+
*/
9+
10+
package org.checkstyle.autofix.recipe.redundantimport.duplicateimport;
11+
12+
import static java.util.Arrays.asList;
13+
import static java.util.Arrays.asList; // violation 'Duplicate import'
14+
15+
import java.util.List;
16+
import java.util.List; // violation 'Duplicate import'
17+
import java.util.Set;
18+
19+
public class InputDuplicateImport {}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*xml
2+
<module name="Checker">
3+
<module name="TreeWalker">
4+
<module name="com.puppycrawl.tools.checkstyle.checks.imports.RedundantImportCheck"/>
5+
</module>
6+
</module>
7+
8+
*/
9+
10+
package org.checkstyle.autofix.recipe.redundantimport.duplicateimport;
11+
12+
import static java.util.Arrays.asList;
13+
14+
import java.util.List;
15+
import java.util.Set;
16+
17+
public class OutputDuplicateImport {}

0 commit comments

Comments
 (0)