diff --git a/src/main/java/org/checkstyle/autofix/PositionHelper.java b/src/main/java/org/checkstyle/autofix/PositionHelper.java new file mode 100644 index 0000000..5d21f88 --- /dev/null +++ b/src/main/java/org/checkstyle/autofix/PositionHelper.java @@ -0,0 +1,96 @@ +/////////////////////////////////////////////////////////////////////////////////////////////// +// checkstyle-openrewrite-recipes: Automatically fix Checkstyle violations with OpenRewrite. +// Copyright (C) 2025 The Checkstyle OpenRewrite Recipes Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +/////////////////////////////////////////////////////////////////////////////////////////////// + +package org.checkstyle.autofix; + +import java.util.concurrent.CancellationException; +import java.util.function.Function; + +import org.openrewrite.Cursor; +import org.openrewrite.PrintOutputCapture; +import org.openrewrite.TreeVisitor; +import org.openrewrite.internal.RecipeRunException; +import org.openrewrite.java.tree.J; + +public final class PositionHelper { + + private PositionHelper() { + // Utility class + } + + public static int computeLinePosition(J tree, J targetElement, Cursor cursor) { + return computePosition(tree, targetElement, cursor, + out -> 1 + Math.toIntExact(out.chars().filter(chr -> chr == '\n').count())); + } + + public static int computeColumnPosition(J tree, J targetElement, Cursor cursor) { + return computePosition(tree, targetElement, cursor, PositionHelper::calculateColumnOffset); + } + + private static int computePosition( + J tree, + J targetElement, + Cursor cursor, + Function positionCalculator + ) { + final TreeVisitor>> printer = + tree.printer(cursor); + + final PrintOutputCapture> capture = + new PrintOutputCapture<>(printer) { + @Override + public PrintOutputCapture> append(String text) { + if (targetElement.isScope(getContext().getCursor().getValue())) { + super.append(targetElement.getPrefix().getWhitespace()); + throw new CancellationException(); + } + return super.append(text); + } + }; + + final int result; + try { + printer.visit(tree, capture, cursor.getParentOrThrow()); + throw new IllegalStateException("Target element: " + targetElement + + ", not found in the syntax tree."); + } + catch (CancellationException exception) { + result = positionCalculator.apply(capture.getOut()); + } + catch (RecipeRunException exception) { + if (exception.getCause() instanceof CancellationException) { + result = positionCalculator.apply(capture.getOut()); + } + else { + throw exception; + } + } + return result; + } + + private static int calculateColumnOffset(String out) { + final int lineBreakIndex = out.lastIndexOf('\n'); + final int result; + if (lineBreakIndex == -1) { + result = out.length(); + } + else { + result = out.length() - lineBreakIndex - 1; + } + return result; + } +} diff --git a/src/main/java/org/checkstyle/autofix/recipe/UpperEll.java b/src/main/java/org/checkstyle/autofix/recipe/UpperEll.java index a46863c..ea0facd 100644 --- a/src/main/java/org/checkstyle/autofix/recipe/UpperEll.java +++ b/src/main/java/org/checkstyle/autofix/recipe/UpperEll.java @@ -19,16 +19,12 @@ import java.nio.file.Path; import java.util.List; -import java.util.concurrent.CancellationException; -import java.util.function.Function; +import org.checkstyle.autofix.PositionHelper; import org.checkstyle.autofix.parser.CheckstyleViolation; -import org.openrewrite.Cursor; import org.openrewrite.ExecutionContext; -import org.openrewrite.PrintOutputCapture; import org.openrewrite.Recipe; import org.openrewrite.TreeVisitor; -import org.openrewrite.internal.RecipeRunException; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.tree.J; import org.openrewrite.java.tree.JavaType; @@ -93,8 +89,8 @@ && isAtViolationLocation(result)) { private boolean isAtViolationLocation(J.Literal literal) { final J.CompilationUnit cursor = getCursor().firstEnclosing(J.CompilationUnit.class); - final int line = computeLinePosition(cursor, literal, getCursor()); - final int column = computeColumnPosition(cursor, literal, getCursor()); + final int line = PositionHelper.computeLinePosition(cursor, literal, getCursor()); + final int column = PositionHelper.computeColumnPosition(cursor, literal, getCursor()); return violations.stream().anyMatch(violation -> { return violation.getLine() == line @@ -102,83 +98,5 @@ private boolean isAtViolationLocation(J.Literal literal) { && Path.of(violation.getFileName()).equals(sourcePath); }); } - - /** - * Computes the position of a target element within a syntax tree using position calculator. - * This method traverses the given syntax tree and captures the printed output until the - * target element is encountered. When the target is found, a CancellationException - * is thrown to interrupt traversal, and the captured output is passed to the provided - * positionCalculator to compute the position. - * - * @param tree the root of the syntax tree to traverse - * @param targetElement the element whose position is to be computed - * @param cursor the current cursor in the tree traversal - * @param positionCalculator a function to compute the position from the printed output - * @return the computed position of the target element - * @throws IllegalStateException if the target element is not found in the tree - * @throws RecipeRunException if an error occurs during traversal - */ - private int computePosition( - J tree, - J targetElement, - Cursor cursor, - Function positionCalculator - ) { - final TreeVisitor>> printer = - tree.printer(cursor); - - final PrintOutputCapture> capture = - new PrintOutputCapture<>(printer) { - @Override - public PrintOutputCapture> append(String text) { - if (targetElement.isScope(getContext().getCursor().getValue())) { - super.append(targetElement.getPrefix().getWhitespace()); - throw new CancellationException(); - } - return super.append(text); - } - }; - - final int result; - try { - printer.visit(tree, capture, cursor.getParentOrThrow()); - throw new IllegalStateException("Target element: " + targetElement - + ", not found in the syntax tree."); - } - catch (CancellationException exception) { - result = positionCalculator.apply(capture.getOut()); - } - catch (RecipeRunException exception) { - if (exception.getCause() instanceof CancellationException) { - result = positionCalculator.apply(capture.getOut()); - } - else { - throw exception; - } - } - return result; - } - - private int computeLinePosition(J tree, J targetElement, Cursor cursor) { - return computePosition(tree, targetElement, cursor, - out -> 1 + Math.toIntExact(out.chars().filter(chr -> chr == '\n').count())); - } - - private int computeColumnPosition(J tree, J targetElement, Cursor cursor) { - return computePosition(tree, targetElement, cursor, this::calculateColumnOffset); - } - - private int calculateColumnOffset(String out) { - final int lineBreakIndex = out.lastIndexOf('\n'); - final int result; - if (lineBreakIndex == -1) { - result = out.length(); - } - else { - result = out.length() - lineBreakIndex - 1; - } - return result; - } - } }