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,33 +159,96 @@ 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 )) {
87- final List <J .Modifier > modifiers = new ArrayList <>();
162+ if (variable .getMarkers ().findFirst (FinalLocalVariableMarker .class ).isPresent ()) {
163+ declarations = addFinalModifier (declarations );
164+ }
165+ }
166+ return declarations ;
167+ }
168+
169+ @ Override
170+ public J .Block visitBlock (J .Block block , ExecutionContext executionContext ) {
171+ final J .Block visited = super .visitBlock (block , executionContext );
88172
89- final Space finalPrefix = declarations . getTypeExpression (). getPrefix ();
173+ final List < Statement > newStatements = new ArrayList <> ();
90174
91- modifiers . add ( new J . Modifier ( Tree . randomId (), finalPrefix ,
92- Markers . EMPTY , null , J . Modifier . Type . Final , new ArrayList <>()));
93- modifiers . addAll ( declarations . getModifiers () );
94- declarations = declarations . withModifiers ( modifiers )
95- . withTypeExpression ( declarations . getTypeExpression ()
96- . withPrefix ( Space . SINGLE_SPACE ) );
175+ for ( Statement stmt : visited . getStatements ()) {
176+ if ( isVariableDeclaration ( stmt )) {
177+ handleMultiVariableDeclaration (( J . VariableDeclarations ) stmt , newStatements );
178+ }
179+ else {
180+ newStatements . add ( stmt );
97181 }
98182 }
99- return declarations ;
183+
184+ return visited .withStatements (newStatements );
100185 }
101186
102- private boolean isAtViolationLocation (J .VariableDeclarations .NamedVariable literal ) {
103- final J .CompilationUnit cursor = getCursor ().firstEnclosing (J .CompilationUnit .class );
187+ private void handleMultiVariableDeclaration (J .VariableDeclarations varDecl ,
188+ List <Statement > newStatements ) {
189+ final List <J .VariableDeclarations .NamedVariable > violationsList = new ArrayList <>();
190+ final List <J .VariableDeclarations .NamedVariable > nonViolations = new ArrayList <>();
104191
105- final int line = PositionHelper .computeLinePosition (cursor , literal , getCursor ());
106- final int column = PositionHelper .computeColumnPosition (cursor , literal , getCursor ());
192+ for (J .VariableDeclarations .NamedVariable variable : varDecl .getVariables ()) {
193+ if (variable .getMarkers ().findFirst (FinalLocalVariableMarker .class ).isPresent ()) {
194+ violationsList .add (variable .withPrefix (Space .SINGLE_SPACE ));
195+ }
196+ else {
197+ nonViolations .add (variable .withPrefix (Space .SINGLE_SPACE ));
198+ }
199+ }
200+ if (violationsList .isEmpty ()) {
201+ newStatements .add (varDecl );
202+ }
203+ else if (nonViolations .isEmpty ()) {
204+ newStatements .add (addFinalModifier (varDecl ));
205+ }
206+ else {
207+ newStatements .add (varDecl .withVariables (nonViolations ));
208+ for (J .VariableDeclarations .NamedVariable variable : violationsList ) {
209+ newStatements .add (addFinalModifier (varDecl
210+ .withVariables (Collections .singletonList (variable ))));
211+ }
212+ }
213+ }
107214
108- return violations .stream ().anyMatch (violation -> {
109- return violation .getLine () == line
110- && violation .getColumn () == column
111- && Path .of (violation .getFileName ()).endsWith (sourcePath );
112- });
215+ private J .VariableDeclarations addFinalModifier (J .VariableDeclarations varDecl ) {
216+ final List <J .Modifier > modifiers = new ArrayList <>();
217+ final Space finalPrefix = varDecl .getTypeExpression ().getPrefix ();
218+ modifiers .add (new J .Modifier (Tree .randomId (), finalPrefix ,
219+ Markers .EMPTY , null , J .Modifier .Type .Final , new ArrayList <>()));
220+
221+ modifiers .addAll (varDecl .getModifiers ());
222+
223+ return varDecl .withModifiers (modifiers )
224+ .withTypeExpression (varDecl .getTypeExpression ().withPrefix (Space .SINGLE_SPACE ));
225+ }
226+
227+ private boolean isVariableDeclaration (Statement stmt ) {
228+ return stmt instanceof J .VariableDeclarations varDecl
229+ && varDecl .getVariables ().size () > 1
230+ && !varDecl .hasModifier (J .Modifier .Type .Final )
231+ && varDecl .getTypeExpression () != null
232+ && !(getCursor ().getParentTreeCursor ()
233+ .getValue () instanceof J .ClassDeclaration );
234+ }
235+ }
236+
237+ public static class FinalLocalVariableMarker implements Marker {
238+ private final UUID id ;
239+
240+ public FinalLocalVariableMarker (UUID uuid ) {
241+ this .id = uuid ;
242+ }
243+
244+ @ Override
245+ public UUID getId () {
246+ return id ;
247+ }
248+
249+ @ Override
250+ public <M extends Marker > M withId (UUID uuid ) {
251+ return (M ) new FinalLocalVariableMarker (uuid );
113252 }
114253 }
115254}
0 commit comments