1919
2020import java .nio .file .Path ;
2121import java .util .ArrayList ;
22+ import java .util .Collections ;
2223import java .util .List ;
24+ import java .util .UUID ;
2325
2426import org .checkstyle .autofix .PositionHelper ;
2527import org .checkstyle .autofix .parser .CheckstyleViolation ;
3032import org .openrewrite .java .JavaIsoVisitor ;
3133import org .openrewrite .java .tree .J ;
3234import org .openrewrite .java .tree .Space ;
35+ import org .openrewrite .java .tree .Statement ;
36+ import org .openrewrite .marker .Marker ;
3337import org .openrewrite .marker .Markers ;
3438
3539/**
@@ -56,17 +60,89 @@ public String getDescription() {
5660
5761 @ Override
5862 public TreeVisitor <?, ExecutionContext > getVisitor () {
59- return new LocalVariableVisitor ();
63+ return new JavaIsoVisitor <>() {
64+ @ Override
65+ public J .CompilationUnit visitCompilationUnit (J .CompilationUnit cu ,
66+ ExecutionContext ctx ) {
67+ J .CompilationUnit compilationUnit = cu ;
68+ compilationUnit = new MarkViolationVisitor ()
69+ .visitCompilationUnit (compilationUnit , ctx );
70+
71+ compilationUnit = new LocalVariableVisitor ()
72+ .visitCompilationUnit (compilationUnit , ctx );
73+
74+ return compilationUnit ;
75+ }
76+ };
6077 }
6178
62- private final class LocalVariableVisitor extends JavaIsoVisitor <ExecutionContext > {
79+ private final class MarkViolationVisitor extends JavaIsoVisitor <ExecutionContext > {
6380
6481 private Path sourcePath ;
82+ private J .CompilationUnit currentCompilationUnit ;
83+
84+ @ Override
85+ public J .CompilationUnit visitCompilationUnit (J .CompilationUnit cu , ExecutionContext ctx ) {
86+ this .sourcePath = cu .getSourcePath ().toAbsolutePath ();
87+ this .currentCompilationUnit = cu ;
88+ return super .visitCompilationUnit (cu , ctx );
89+ }
90+
91+ @ Override
92+ public J .VariableDeclarations visitVariableDeclarations (
93+ J .VariableDeclarations multiVariable , ExecutionContext executionContext ) {
94+
95+ final J .VariableDeclarations variableDeclarations ;
96+
97+ final J .VariableDeclarations declarations = super
98+ .visitVariableDeclarations (multiVariable , executionContext );
99+
100+ if (!(getCursor ().getParentTreeCursor ().getValue () instanceof J .ClassDeclaration )
101+ && !declarations .hasModifier (J .Modifier .Type .Final )) {
102+
103+ final List <J .VariableDeclarations .NamedVariable > variables = declarations
104+ .getVariables ();
105+ final List <J .VariableDeclarations .NamedVariable > marked = new ArrayList <>();
106+ for (J .VariableDeclarations .NamedVariable variable : variables ) {
107+ if (isAtViolationLocation (variable )) {
108+ marked .add (variable .withMarkers (
109+ variable .getMarkers ()
110+ .add (new FinalLocalVariableMarker (UUID .randomUUID ()))));
111+ }
112+ else {
113+ marked .add (variable );
114+ }
115+ }
116+ variableDeclarations = declarations .withVariables (marked );
117+ }
118+ else {
119+ variableDeclarations = declarations ;
120+ }
121+ return variableDeclarations ;
122+ }
123+
124+ private boolean isAtViolationLocation (J .VariableDeclarations .NamedVariable variable ) {
125+
126+ final int line = PositionHelper
127+ .computeLinePosition (currentCompilationUnit , variable , getCursor ());
128+ final int column = PositionHelper
129+ .computeColumnPosition (currentCompilationUnit , variable , getCursor ());
130+
131+ return violations .removeIf (violation -> {
132+ final Path absolutePath = Path .of (violation .getFileName ()).toAbsolutePath ();
133+ return violation .getLine () == line
134+ && violation .getColumn () == column
135+ && absolutePath .endsWith (sourcePath )
136+ && violation .getMessage ().contains (variable .getSimpleName ());
137+ });
138+ }
139+ }
140+
141+ private final class LocalVariableVisitor extends JavaIsoVisitor <ExecutionContext > {
65142
66143 @ Override
67144 public J .CompilationUnit visitCompilationUnit (
68145 J .CompilationUnit cu , ExecutionContext executionContext ) {
69- this .sourcePath = cu .getSourcePath ();
70146 return super .visitCompilationUnit (cu , executionContext );
71147 }
72148
@@ -83,7 +159,7 @@ public J.VariableDeclarations visitVariableDeclarations(
83159 && !declarations .hasModifier (J .Modifier .Type .Final )) {
84160 final J .VariableDeclarations .NamedVariable variable = declarations
85161 .getVariables ().get (0 );
86- if (isAtViolationLocation ( variable )) {
162+ if (variable . getMarkers (). findFirst ( FinalLocalVariableMarker . class ). isPresent ( )) {
87163 final List <J .Modifier > modifiers = new ArrayList <>();
88164
89165 final Space finalPrefix = declarations .getTypeExpression ().getPrefix ();
@@ -99,17 +175,119 @@ public J.VariableDeclarations visitVariableDeclarations(
99175 return declarations ;
100176 }
101177
102- private boolean isAtViolationLocation (J .VariableDeclarations .NamedVariable literal ) {
103- final J .CompilationUnit cursor = getCursor ().firstEnclosing (J .CompilationUnit .class );
178+ @ Override
179+ public J .Block visitBlock (J .Block block , ExecutionContext executionContext ) {
180+ J .Block visited = super .visitBlock (block , executionContext );
104181
105- final int line = PositionHelper . computeLinePosition ( cursor , literal , getCursor () );
106- final int column = PositionHelper . computeColumnPosition ( cursor , literal , getCursor ()) ;
182+ final List < Statement > newStatements = new ArrayList <>( );
183+ boolean changed = false ;
107184
108- return violations .stream ().anyMatch (violation -> {
109- return violation .getLine () == line
110- && violation .getColumn () == column
111- && Path .of (violation .getFileName ()).endsWith (sourcePath );
112- });
185+ for (Statement stmt : visited .getStatements ()) {
186+ if (!(stmt instanceof J .VariableDeclarations )) {
187+ newStatements .add (stmt );
188+ continue ;
189+ }
190+
191+ final J .VariableDeclarations varDecl = (J .VariableDeclarations ) stmt ;
192+
193+ if (varDecl .hasModifier (J .Modifier .Type .Final ) || getCursor ().getParentTreeCursor ()
194+ .getValue () instanceof J .ClassDeclaration ) {
195+ newStatements .add (stmt );
196+ continue ;
197+ }
198+
199+ if (varDecl .getVariables ().size () > 1 ) {
200+ changed |= handleMultiVariableDeclaration (varDecl , newStatements );
201+ }
202+ else {
203+ newStatements .add (stmt );
204+ }
205+ }
206+
207+ if (changed ) {
208+ visited = visited .withStatements (newStatements );
209+ }
210+
211+ return visited ;
212+ }
213+
214+ private boolean handleMultiVariableDeclaration (J .VariableDeclarations varDecl ,
215+ List <Statement > newStatements ) {
216+ final List <J .VariableDeclarations .NamedVariable > violationsList = new ArrayList <>();
217+ final List <J .VariableDeclarations .NamedVariable > nonViolations = new ArrayList <>();
218+
219+ for (J .VariableDeclarations .NamedVariable variable : varDecl .getVariables ()) {
220+ if (variable .getMarkers ().findFirst (FinalLocalVariableMarker .class ).isPresent ()) {
221+ violationsList .add (variable .withPrefix (Space .SINGLE_SPACE ));
222+ }
223+ else {
224+ nonViolations .add (variable .withPrefix (Space .SINGLE_SPACE ));
225+ }
226+ }
227+
228+ boolean changed = false ;
229+
230+ if (violationsList .isEmpty ()) {
231+ newStatements .add (varDecl );
232+ }
233+ else if (nonViolations .isEmpty ()) {
234+ newStatements .add (addFinalModifier (varDecl ));
235+ changed = true ;
236+ }
237+ else {
238+ newStatements .add (varDecl .withVariables (nonViolations ));
239+ for (J .VariableDeclarations .NamedVariable variable : violationsList ) {
240+ newStatements .add (addFinalModifier (varDecl
241+ .withVariables (Collections .singletonList (variable ))));
242+ }
243+ changed = true ;
244+ }
245+
246+ return changed ;
247+ }
248+
249+ private J .VariableDeclarations addFinalModifier (J .VariableDeclarations varDecl ) {
250+ final List <J .Modifier > modifiers = new ArrayList <>();
251+ final Space finalPrefix ;
252+ if (varDecl .getTypeExpression () != null ) {
253+ finalPrefix = varDecl .getTypeExpression ().getPrefix ();
254+ }
255+ else {
256+ finalPrefix = Space .EMPTY ;
257+ }
258+
259+ modifiers .add (new J .Modifier (Tree .randomId (), finalPrefix ,
260+ Markers .EMPTY , null , J .Modifier .Type .Final , new ArrayList <>()));
261+ modifiers .addAll (varDecl .getModifiers ());
262+
263+ J .VariableDeclarations result = varDecl .withModifiers (modifiers );
264+
265+ if (result .getTypeExpression () != null ) {
266+ result = result .withTypeExpression (
267+ result .getTypeExpression ().withPrefix (Space .SINGLE_SPACE ));
268+ }
269+
270+ return result ;
271+ }
272+ }
273+
274+ public static class FinalLocalVariableMarker implements Marker {
275+ private final UUID id ;
276+
277+ public FinalLocalVariableMarker (UUID uuid ) {
278+ this .id = uuid ;
279+ }
280+
281+ @ Override
282+ public UUID getId () {
283+ return id ;
113284 }
285+
286+ @ Override
287+ public <M extends Marker > M withId (UUID uuid ) {
288+ return (M ) new FinalLocalVariableMarker (uuid );
289+ }
290+
114291 }
292+
115293}
0 commit comments