Skip to content

Commit 5e6ab4e

Browse files
committed
Implemented Violations to upperEll recipe
1 parent c909a05 commit 5e6ab4e

File tree

10 files changed

+283
-33
lines changed

10 files changed

+283
-33
lines changed

config/import-control-test.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@
1111
<allow pkg="java.util"/>
1212
<allow pkg="java.nio"/>
1313
<allow pkg="java.lang"/>
14+
<allow pkg="javax.xml.stream"/>
1415
</import-control>

src/main/java/org/checkstyle/autofix/parser/CheckstyleReportsParser.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,7 @@ private CheckstyleReportsParser() {
5555

5656
}
5757

58-
public static List<CheckstyleViolation> parse(Path xmlPath)
59-
throws IOException, XMLStreamException {
58+
public static List<CheckstyleViolation> parse(Path xmlPath) {
6059

6160
final List<CheckstyleViolation> result = new ArrayList<>();
6261

@@ -89,6 +88,11 @@ else if (ERROR_TAG.equals(startElementName)) {
8988
reader.close();
9089
}
9190
}
91+
92+
}
93+
catch (IOException | XMLStreamException exception) {
94+
throw new IllegalArgumentException("Failed to parse checkstyle XML report from: "
95+
+ xmlPath, exception);
9296
}
9397

9498
return result;

src/main/java/org/checkstyle/autofix/recipe/UpperEll.java

Lines changed: 129 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,19 @@
1717

1818
package org.checkstyle.autofix.recipe;
1919

20+
import java.nio.file.Path;
21+
import java.util.ArrayList;
22+
import java.util.List;
23+
import java.util.concurrent.CancellationException;
24+
import java.util.function.Function;
25+
26+
import org.checkstyle.autofix.parser.CheckstyleViolation;
27+
import org.openrewrite.Cursor;
2028
import org.openrewrite.ExecutionContext;
29+
import org.openrewrite.PrintOutputCapture;
2130
import org.openrewrite.Recipe;
2231
import org.openrewrite.TreeVisitor;
32+
import org.openrewrite.internal.RecipeRunException;
2333
import org.openrewrite.java.JavaIsoVisitor;
2434
import org.openrewrite.java.tree.J;
2535
import org.openrewrite.java.tree.JavaType;
@@ -30,6 +40,16 @@
3040
*/
3141
public class UpperEll extends Recipe {
3242

43+
private final List<CheckstyleViolation> violations;
44+
45+
public UpperEll() {
46+
this(new ArrayList<>());
47+
}
48+
49+
public UpperEll(List<CheckstyleViolation> violations) {
50+
this.violations = violations;
51+
}
52+
3353
@Override
3454
public String getDisplayName() {
3555
return "UpperEll recipe";
@@ -46,20 +66,121 @@ public TreeVisitor<?, ExecutionContext> getVisitor() {
4666
return new UpperEllVisitor();
4767
}
4868

49-
/**
50-
* Visitor that replaces lowercase 'l' suffixes in long literals with uppercase 'L'.
51-
*/
52-
private static final class UpperEllVisitor extends JavaIsoVisitor<ExecutionContext> {
69+
private final class UpperEllVisitor extends JavaIsoVisitor<ExecutionContext> {
70+
71+
private static final String LOWERCASE_L = "l";
72+
private static final String UPPERCASE_L = "L";
73+
74+
private Path currentFileName;
75+
76+
@Override
77+
public J.CompilationUnit visitCompilationUnit(J.CompilationUnit cu, ExecutionContext ctx) {
78+
this.currentFileName = cu.getSourcePath();
79+
return super.visitCompilationUnit(cu, ctx);
80+
}
81+
5382
@Override
5483
public J.Literal visitLiteral(J.Literal literal, ExecutionContext ctx) {
5584
J.Literal result = super.visitLiteral(literal, ctx);
5685
final String valueSource = result.getValueSource();
5786

58-
if (valueSource != null && valueSource.endsWith("l")
59-
&& result.getType() == JavaType.Primitive.Long) {
87+
if (valueSource != null && valueSource.endsWith(LOWERCASE_L)
88+
&& result.getType() == JavaType.Primitive.Long
89+
&& isAtViolationLocation(result)) {
90+
6091
final String numericPart = valueSource.substring(0, valueSource.length() - 1);
61-
final String newValueSource = numericPart + "L";
62-
result = result.withValueSource(newValueSource);
92+
result = result.withValueSource(numericPart + UPPERCASE_L);
93+
}
94+
95+
return result;
96+
}
97+
98+
private boolean isAtViolationLocation(J.Literal literal) {
99+
final J.CompilationUnit cursor = getCursor().firstEnclosing(J.CompilationUnit.class);
100+
101+
final int line = computeLinePosition(cursor, literal, getCursor());
102+
final int column = computeColumnPosition(cursor, literal, getCursor());
103+
104+
return violations.stream().anyMatch(violation -> {
105+
return violation.getLine() == line
106+
&& violation.getColumn() == column
107+
&& Path.of(violation.getFileName()).equals(currentFileName);
108+
});
109+
}
110+
111+
/**
112+
* Computes the position of a target element within a syntax tree using position calculator.
113+
* This method traverses the given syntax tree and captures the printed output until the
114+
* target element is encountered. When the target is found, a CancellationException
115+
* is thrown to interrupt traversal, and the captured output is passed to the provided
116+
* positionCalculator to compute the position.
117+
*
118+
* @param tree the root of the syntax tree to traverse
119+
* @param targetElement the element whose position is to be computed
120+
* @param cursor the current cursor in the tree traversal
121+
* @param positionCalculator a function to compute the position from the printed output
122+
* @return the computed position of the target element
123+
* @throws IllegalStateException if the target element is not found in the tree
124+
* @throws RecipeRunException if an error occurs during traversal
125+
*/
126+
private int computePosition(
127+
J tree,
128+
J targetElement,
129+
Cursor cursor,
130+
Function<String, Integer> positionCalculator
131+
) {
132+
final TreeVisitor<?, PrintOutputCapture<TreeVisitor<?, ?>>> printer =
133+
tree.printer(cursor);
134+
135+
final PrintOutputCapture<TreeVisitor<?, ?>> capture =
136+
new PrintOutputCapture<>(printer) {
137+
@Override
138+
public PrintOutputCapture<TreeVisitor<?, ?>> append(String text) {
139+
if (targetElement.isScope(getContext().getCursor().getValue())) {
140+
super.append(targetElement.getPrefix().getWhitespace());
141+
throw new CancellationException();
142+
}
143+
return super.append(text);
144+
}
145+
};
146+
147+
final int result;
148+
try {
149+
printer.visit(tree, capture, cursor.getParentOrThrow());
150+
throw new IllegalStateException("Target element: " + targetElement
151+
+ ", not found in the syntax tree.");
152+
}
153+
catch (CancellationException exception) {
154+
result = positionCalculator.apply(capture.getOut());
155+
}
156+
catch (RecipeRunException exception) {
157+
if (exception.getCause() instanceof CancellationException) {
158+
result = positionCalculator.apply(capture.getOut());
159+
}
160+
else {
161+
throw exception;
162+
}
163+
}
164+
return result;
165+
}
166+
167+
private int computeLinePosition(J tree, J targetElement, Cursor cursor) {
168+
return computePosition(tree, targetElement, cursor,
169+
out -> 1 + Math.toIntExact(out.chars().filter(chr -> chr == '\n').count()));
170+
}
171+
172+
private int computeColumnPosition(J tree, J targetElement, Cursor cursor) {
173+
return computePosition(tree, targetElement, cursor, this::calculateColumnOffset);
174+
}
175+
176+
private int calculateColumnOffset(String out) {
177+
final int lineBreakIndex = out.lastIndexOf('\n');
178+
final int result;
179+
if (lineBreakIndex == -1) {
180+
result = out.length();
181+
}
182+
else {
183+
result = out.length() - lineBreakIndex - 1;
63184
}
64185
return result;
65186
}

src/test/java/org/checkstyle/autofix/recipe/AbstractRecipeTest.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
import java.nio.file.Files;
2525
import java.nio.file.Paths;
2626

27+
import javax.xml.stream.XMLStreamException;
28+
2729
import org.checkstyle.autofix.InputClassRenamer;
2830
import org.openrewrite.Recipe;
2931
import org.openrewrite.test.RewriteTest;
@@ -53,7 +55,7 @@ private Recipe createPreprocessingRecipe() {
5355
*
5456
* @return the main recipe to test
5557
*/
56-
protected abstract Recipe getRecipe();
58+
protected abstract Recipe getRecipe() throws XMLStreamException, IOException;
5759

5860
/**
5961
* Tests a recipe with the given recipe path and test case name.
@@ -67,7 +69,8 @@ private Recipe createPreprocessingRecipe() {
6769
* @param testCaseName the name of the test case (should match directory and file names)
6870
* @throws IOException if files cannot be read
6971
*/
70-
protected void testRecipe(String recipePath, String testCaseName) throws IOException {
72+
protected void testRecipe(String recipePath, String testCaseName)
73+
throws IOException, XMLStreamException {
7174
final String testCaseDir = testCaseName.toLowerCase();
7275
final String inputFileName = "Input" + testCaseName + ".java";
7376
final String outputFileName = "Output" + testCaseName + ".java";

src/test/java/org/checkstyle/autofix/recipe/UpperEllTest.java

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,29 +18,40 @@
1818
package org.checkstyle.autofix.recipe;
1919

2020
import java.io.IOException;
21+
import java.nio.file.Path;
22+
import java.util.List;
2123

24+
import javax.xml.stream.XMLStreamException;
25+
26+
import org.checkstyle.autofix.parser.CheckstyleReportsParser;
27+
import org.checkstyle.autofix.parser.CheckstyleViolation;
2228
import org.junit.jupiter.api.Test;
2329
import org.openrewrite.Recipe;
2430

2531
public class UpperEllTest extends AbstractRecipeTest {
2632

2733
@Override
28-
protected Recipe getRecipe() {
29-
return new UpperEll();
34+
protected Recipe getRecipe() throws XMLStreamException, IOException {
35+
final String reportPath = "src/test/resources/org/checkstyle/autofix/recipe/upperell"
36+
+ "/report.xml";
37+
38+
final List<CheckstyleViolation> violations =
39+
CheckstyleReportsParser.parse(Path.of(reportPath));
40+
return new UpperEll(violations);
3041
}
3142

3243
@Test
33-
void hexOctalLiteralTest() throws IOException {
44+
void hexOctalLiteralTest() throws IOException, XMLStreamException {
3445
testRecipe("upperell", "HexOctalLiteral");
3546
}
3647

3748
@Test
38-
void complexLongLiterals() throws IOException {
49+
void complexLongLiterals() throws IOException, XMLStreamException {
3950
testRecipe("upperell", "ComplexLongLiterals");
4051
}
4152

4253
@Test
43-
void stringAndCommentTest() throws IOException {
54+
void stringAndCommentTest() throws IOException, XMLStreamException {
4455
testRecipe("upperell", "StringAndComments");
4556
}
4657
}

src/test/resources/org/checkstyle/autofix/recipe/upperell/complexlongliterals/InputComplexLongLiterals.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ public class InputComplexLongLiterals {
1111
private Long simpleLong = 1234l;
1212
private Long negativeLong = -5678l;
1313
private Long underscoreLong = 1_000_000l;
14-
Long maxLongObject = 9223372036854775807l;
15-
Long minLongObject = -9223372036854775808l;
14+
Long maxLongObject = 9223372036854775807l; //suppressed violation
15+
Long minLongObject = -9223372036854775808l; //suppressed violation
1616

1717
public long calculate(long input1, long input2) {
1818
return input1 + input2 + 1000l;

src/test/resources/org/checkstyle/autofix/recipe/upperell/complexlongliterals/OutputComplexLongLiterals.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ public class OutputComplexLongLiterals {
1111
private Long simpleLong = 1234L;
1212
private Long negativeLong = -5678L;
1313
private Long underscoreLong = 1_000_000L;
14-
Long maxLongObject = 9223372036854775807L;
15-
Long minLongObject = -9223372036854775808L;
14+
Long maxLongObject = 9223372036854775807l; //suppressed violation
15+
Long minLongObject = -9223372036854775808l; //suppressed violation
1616

1717
public long calculate(long input1, long input2) {
1818
return input1 + input2 + 1000L;

src/test/resources/org/checkstyle/autofix/recipe/upperell/hexoctalliteral/InputHexOctalLiteral.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22

33
public class InputHexOctalLiteral {
44
private long hexLower = 0x1ABCl;
5-
private long hexUpper = 0X2DEFl;
6-
private long octal = 0777l;
7-
private long binary = 0b1010l;
8-
private long decimal = 12345l;
5+
private long hexUpper = 0X2DEFl;
6+
private long octal = 0777l;
7+
private long binary = 0b1010l;
8+
private long decimal = 12345l;
99

1010
public void calculateValues() {
11-
long hexResult = 0xDEADBEEFl;
12-
long octalResult = 01234l;
11+
long hexResult = 0xDEADBEEFl + 0xDEADBEFl; //suppressed violation for 0xDEADBEFl
12+
long octalResult = 01234l + 0xDEADBEEFl; //suppressed violation for 0xDEADBEFl
1313
long binaryResult = 0b11110000l;
1414
}
1515
}

src/test/resources/org/checkstyle/autofix/recipe/upperell/hexoctalliteral/OutputHexOctalLiteral.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22

33
public class OutputHexOctalLiteral {
44
private long hexLower = 0x1ABCL;
5-
private long hexUpper = 0X2DEFL;
6-
private long octal = 0777L;
7-
private long binary = 0b1010L;
8-
private long decimal = 12345L;
5+
private long hexUpper = 0X2DEFL;
6+
private long octal = 0777L;
7+
private long binary = 0b1010L;
8+
private long decimal = 12345L;
99

1010
public void calculateValues() {
11-
long hexResult = 0xDEADBEEFL;
12-
long octalResult = 01234L;
11+
long hexResult = 0xDEADBEEFL + 0xDEADBEFl; //suppressed violation for 0xDEADBEFl
12+
long octalResult = 01234L + 0xDEADBEEFl; //suppressed violation for 0xDEADBEFl
1313
long binaryResult = 0b11110000L;
1414
}
1515
}

0 commit comments

Comments
 (0)