2020import org .jspecify .annotations .Nullable ;
2121import org .openrewrite .*;
2222import org .openrewrite .gradle .internal .ChangeStringLiteral ;
23- import org .openrewrite .groovy .GroovyIsoVisitor ;
2423import org .openrewrite .groovy .tree .G ;
2524import org .openrewrite .internal .ListUtils ;
2625import org .openrewrite .java .JavaIsoVisitor ;
2726import org .openrewrite .java .MethodMatcher ;
2827import org .openrewrite .java .tree .*;
29- import org .openrewrite .kotlin .KotlinIsoVisitor ;
3028import org .openrewrite .kotlin .KotlinParser ;
3129import org .openrewrite .kotlin .tree .K ;
3230import org .openrewrite .marker .Markers ;
4442public class UpdateJavaCompatibility extends Recipe {
4543 private static final MethodMatcher SOURCE_COMPATIBILITY_DSL = new MethodMatcher ("org.gradle.api.Project setSourceCompatibility(..)" , true );
4644 private static final MethodMatcher TARGET_COMPATIBILITY_DSL = new MethodMatcher ("org.gradle.api.Project setTargetCompatibility(..)" , true );
45+ private static final MethodMatcher SOURCE_COMPATIBILITY_METHOD = new MethodMatcher ("org.gradle.api.Project sourceCompatibility(..)" , true );
46+ private static final MethodMatcher TARGET_COMPATIBILITY_METHOD = new MethodMatcher ("org.gradle.api.Project targetCompatibility(..)" , true );
4747
4848 @ Option (displayName = "Java version" ,
4949 description = "The Java version to upgrade to." ,
@@ -78,9 +78,6 @@ public class UpdateJavaCompatibility extends Recipe {
7878 @ Nullable
7979 Boolean addIfMissing ;
8080
81- private static final String SOURCE_COMPATIBILITY_FOUND = "SOURCE_COMPATIBILITY_FOUND" ;
82- private static final String TARGET_COMPATIBILITY_FOUND = "TARGET_COMPATIBILITY_FOUND" ;
83-
8481 String displayName = "Update Gradle project Java compatibility" ;
8582
8683 String description = "Find and updates the Java compatibility for the Gradle project." ;
@@ -92,118 +89,124 @@ public Validated<Object> validate() {
9289
9390 @ Override
9491 public TreeVisitor <?, ExecutionContext > getVisitor () {
95- return Preconditions .check (new IsBuildGradle <>(), Preconditions .or (new GroovyScriptVisitor (), new KotlinScriptVisitor ()));
96- }
97-
98- private class GroovyScriptVisitor extends GroovyIsoVisitor <ExecutionContext > {
99- @ Override
100- public G .CompilationUnit visitCompilationUnit (G .CompilationUnit cu , ExecutionContext ctx ) {
101- G .CompilationUnit c = super .visitCompilationUnit (cu , ctx );
102- if (getCursor ().pollMessage (SOURCE_COMPATIBILITY_FOUND ) == null ) {
103- c = addCompatibilityTypeToSourceFile (c , "source" , ctx );
104- }
105- if (getCursor ().pollMessage (TARGET_COMPATIBILITY_FOUND ) == null ) {
106- c = addCompatibilityTypeToSourceFile (c , "target" , ctx );
92+ return Preconditions .check (new IsBuildGradle <>(), new JavaIsoVisitor <ExecutionContext >() {
93+ boolean sourceCompatibilityFound ;
94+ boolean targetCompatibilityFound ;
95+
96+ @ Override
97+ public @ Nullable J visit (@ Nullable Tree tree , ExecutionContext ctx ) {
98+ if (!(tree instanceof SourceFile )) {
99+ return super .visit (tree , ctx );
100+ }
101+ sourceCompatibilityFound = false ;
102+ targetCompatibilityFound = false ;
103+ J visited = super .visit (tree , ctx );
104+ if (visited instanceof G .CompilationUnit ) {
105+ G .CompilationUnit c = (G .CompilationUnit ) visited ;
106+ if (!sourceCompatibilityFound ) {
107+ c = addGroovyCompatibilityType (c , "source" , ctx );
108+ }
109+ if (!targetCompatibilityFound ) {
110+ c = addGroovyCompatibilityType (c , "target" , ctx );
111+ }
112+ return c ;
113+ } else if (visited instanceof K .CompilationUnit ) {
114+ K .CompilationUnit c = (K .CompilationUnit ) visited ;
115+ if (!sourceCompatibilityFound ) {
116+ c = addKotlinCompatibilityType (c , "source" , ctx );
117+ }
118+ if (!targetCompatibilityFound ) {
119+ c = addKotlinCompatibilityType (c , "target" , ctx );
120+ }
121+ return c ;
122+ }
123+ return visited ;
107124 }
108- return c ;
109- }
110-
111- @ Override
112- public J .Assignment visitAssignment (J .Assignment assignment , ExecutionContext ctx ) {
113- return handleAssignment (super .visitAssignment (assignment , ctx ), getCursor (), G .CompilationUnit .class );
114- }
115125
116- @ Override
117- public J .MethodInvocation visitMethodInvocation (J .MethodInvocation method , ExecutionContext ctx ) {
118- return handleMethodInvocation (super .visitMethodInvocation (method , ctx ), getCursor (), G .CompilationUnit .class );
119- }
126+ @ Override
127+ public J .Assignment visitAssignment (J .Assignment assignment , ExecutionContext ctx ) {
128+ J .Assignment a = super .visitAssignment (assignment , ctx );
129+ if (a .getVariable () instanceof J .Identifier ) {
130+ J .Identifier variable = (J .Identifier ) a .getVariable ();
131+ if ("sourceCompatibility" .equals (variable .getSimpleName ())) {
132+ sourceCompatibilityFound = true ;
133+ }
134+ if ("targetCompatibility" .equals (variable .getSimpleName ())) {
135+ targetCompatibilityFound = true ;
136+ }
137+ }
138+ return handleAssignment (a );
139+ }
120140
121- private G .CompilationUnit addCompatibilityTypeToSourceFile (G .CompilationUnit c , String targetCompatibilityType , ExecutionContext ctx ) {
122- if ((compatibilityType == null || targetCompatibilityType .equals (compatibilityType .toString ())) && TRUE .equals (addIfMissing )) {
123- G .CompilationUnit sourceFile = (G .CompilationUnit ) GradleParser .builder ().build ()
124- .parse (ctx , "\n " + targetCompatibilityType + "Compatibility = " + styleMissingCompatibilityVersion (declarationStyle ))
125- .findFirst ()
126- .orElseThrow (() -> new IllegalStateException ("Unable to parse compatibility type as a Gradle file" ));
127- c = c .withStatements (ListUtils .concatAll (c .getStatements (), sourceFile .getStatements ()));
141+ @ Override
142+ public J .MethodInvocation visitMethodInvocation (J .MethodInvocation method , ExecutionContext ctx ) {
143+ J .MethodInvocation m = super .visitMethodInvocation (method , ctx );
144+ if (SOURCE_COMPATIBILITY_METHOD .matches (m ) || SOURCE_COMPATIBILITY_DSL .matches (m )) {
145+ sourceCompatibilityFound = true ;
146+ }
147+ if (TARGET_COMPATIBILITY_METHOD .matches (m ) || TARGET_COMPATIBILITY_DSL .matches (m )) {
148+ targetCompatibilityFound = true ;
149+ }
150+ return handleMethodInvocation (m );
128151 }
129- return c ;
130- }
152+ });
131153 }
132154
133- private class KotlinScriptVisitor extends KotlinIsoVisitor <ExecutionContext > {
134- @ Override
135- public K .CompilationUnit visitCompilationUnit (K .CompilationUnit cu , ExecutionContext ctx ) {
136- K .CompilationUnit c = super .visitCompilationUnit (cu , ctx );
137- if (getCursor ().pollMessage (SOURCE_COMPATIBILITY_FOUND ) == null ) {
138- c = addCompatibilityTypeToSourceFile (c , "source" , ctx );
139- }
140- if (getCursor ().pollMessage (TARGET_COMPATIBILITY_FOUND ) == null ) {
141- c = addCompatibilityTypeToSourceFile (c , "target" , ctx );
142- }
143- return super .visitCompilationUnit (c , ctx );
155+ private G .CompilationUnit addGroovyCompatibilityType (G .CompilationUnit c , String targetCompatibilityType , ExecutionContext ctx ) {
156+ if ((compatibilityType == null || targetCompatibilityType .equals (compatibilityType .toString ())) && TRUE .equals (addIfMissing )) {
157+ G .CompilationUnit sourceFile = (G .CompilationUnit ) GradleParser .builder ().build ()
158+ .parse (ctx , "\n " + targetCompatibilityType + "Compatibility = " + styleMissingCompatibilityVersion (declarationStyle ))
159+ .findFirst ()
160+ .orElseThrow (() -> new IllegalStateException ("Unable to parse compatibility type as a Gradle file" ));
161+ c = c .withStatements (ListUtils .concatAll (c .getStatements (), sourceFile .getStatements ()));
144162 }
163+ return c ;
164+ }
145165
146- @ Override
147- public J .Assignment visitAssignment (J .Assignment assignment , ExecutionContext ctx ) {
148- return handleAssignment (super .visitAssignment (assignment , ctx ), getCursor (), K .CompilationUnit .class );
149- }
166+ private K .CompilationUnit addKotlinCompatibilityType (K .CompilationUnit c , String targetCompatibilityType , ExecutionContext ctx ) {
167+ if ((compatibilityType == null || targetCompatibilityType .equals (compatibilityType .toString ())) && TRUE .equals (addIfMissing )) {
168+ J withExistingJavaMethod = maybeAddToExistingJavaMethod (c , targetCompatibilityType , ctx );
169+ if (withExistingJavaMethod != c ) {
170+ return (K .CompilationUnit ) withExistingJavaMethod ;
171+ }
150172
151- @ Override
152- public J .MethodInvocation visitMethodInvocation (J .MethodInvocation method , ExecutionContext ctx ) {
153- return handleMethodInvocation (super .visitMethodInvocation (method , ctx ), getCursor (), K .CompilationUnit .class );
173+ K .CompilationUnit sourceFile = (K .CompilationUnit ) KotlinParser .builder ()
174+ .isKotlinScript (true )
175+ .build ().parse (ctx , "\n \n java {\n " + targetCompatibilityType + "Compatibility = " + styleMissingCompatibilityVersion (DeclarationStyle .Enum ) + "\n }" )
176+ .findFirst ()
177+ .orElseThrow (() -> new IllegalStateException ("Unable to parse compatibility type as a Gradle file" ));
178+ c = c .withStatements (ListUtils .concatAll (c .getStatements (), sourceFile .getStatements ()));
154179 }
180+ return c ;
181+ }
155182
156- private K .CompilationUnit addCompatibilityTypeToSourceFile (K .CompilationUnit c , String targetCompatibilityType , ExecutionContext ctx ) {
157- if ((compatibilityType == null || targetCompatibilityType .equals (compatibilityType .toString ())) && TRUE .equals (addIfMissing )) {
158- J withExistingJavaMethod = maybeAddToExistingJavaMethod (c , targetCompatibilityType , ctx );
159- if (withExistingJavaMethod != c ) {
160- return (K .CompilationUnit ) withExistingJavaMethod ;
183+ private J maybeAddToExistingJavaMethod (K .CompilationUnit c , String compatibilityType , ExecutionContext ctx ) {
184+ return new JavaIsoVisitor <ExecutionContext >() {
185+ @ Override
186+ public J .MethodInvocation visitMethodInvocation (J .MethodInvocation method , ExecutionContext ctx ) {
187+ if ("java" .equals (method .getSimpleName ())) {
188+ return new JavaIsoVisitor <ExecutionContext >() {
189+ @ Override
190+ public J .Lambda visitLambda (J .Lambda lambda , ExecutionContext ctx ) {
191+ J .Block body = (J .Block ) lambda .getBody ();
192+ List <Statement > statements = body .getStatements ();
193+ K .CompilationUnit sourceFile = (K .CompilationUnit ) KotlinParser .builder ()
194+ .isKotlinScript (true )
195+ .build ().parse (ctx , "\n " + compatibilityType + "Compatibility = " + styleMissingCompatibilityVersion (DeclarationStyle .Enum ))
196+ .findFirst ()
197+ .orElseThrow (() -> new IllegalStateException ("Unable to parse compatibility type as a Gradle file" ));
198+ return lambda .withBody (body .withStatements (ListUtils .concatAll (statements , sourceFile .getStatements ())));
199+ }
200+ }.visitMethodInvocation (method , ctx );
161201 }
162-
163- K .CompilationUnit sourceFile = (K .CompilationUnit ) KotlinParser .builder ()
164- .isKotlinScript (true )
165- .build ().parse (ctx , "\n \n java {\n " + targetCompatibilityType + "Compatibility = " + styleMissingCompatibilityVersion (DeclarationStyle .Enum ) + "\n }" )
166- .findFirst ()
167- .orElseThrow (() -> new IllegalStateException ("Unable to parse compatibility type as a Gradle file" ));
168- c = c .withStatements (ListUtils .concatAll (c .getStatements (), sourceFile .getStatements ()));
202+ return super .visitMethodInvocation (method , ctx );
169203 }
170- return c ;
171- }
172-
173- private J maybeAddToExistingJavaMethod (K .CompilationUnit c , String compatibilityType , ExecutionContext ctx ) {
174- return new JavaIsoVisitor <ExecutionContext >() {
175- @ Override
176- public J .MethodInvocation visitMethodInvocation (J .MethodInvocation method , ExecutionContext ctx ) {
177- if ("java" .equals (method .getSimpleName ())) {
178- return new JavaIsoVisitor <ExecutionContext >() {
179- @ Override
180- public J .Lambda visitLambda (J .Lambda lambda , ExecutionContext ctx ) {
181- J .Block body = (J .Block ) lambda .getBody ();
182- List <Statement > statements = body .getStatements ();
183- K .CompilationUnit sourceFile = (K .CompilationUnit ) KotlinParser .builder ()
184- .isKotlinScript (true )
185- .build ().parse (ctx , "\n " + compatibilityType + "Compatibility = " + styleMissingCompatibilityVersion (DeclarationStyle .Enum ))
186- .findFirst ()
187- .orElseThrow (() -> new IllegalStateException ("Unable to parse compatibility type as a Gradle file" ));
188- return lambda .withBody (body .withStatements (ListUtils .concatAll (statements , sourceFile .getStatements ())));
189- }
190- }.visitMethodInvocation (method , ctx );
191- }
192- return super .visitMethodInvocation (method , ctx );
193- }
194- }.visitNonNull (c , ctx );
195- }
204+ }.visitNonNull (c , ctx );
196205 }
197206
198- public J .Assignment handleAssignment (J .Assignment a , Cursor c , Class <?> enclosing ) {
207+ private J .Assignment handleAssignment (J .Assignment a ) {
199208 if (a .getVariable () instanceof J .Identifier ) {
200209 J .Identifier variable = (J .Identifier ) a .getVariable ();
201- if ("sourceCompatibility" .equals (variable .getSimpleName ())) {
202- c .putMessageOnFirstEnclosing (enclosing , SOURCE_COMPATIBILITY_FOUND , true );
203- }
204- if ("targetCompatibility" .equals (variable .getSimpleName ())) {
205- c .putMessageOnFirstEnclosing (enclosing , TARGET_COMPATIBILITY_FOUND , true );
206- }
207210
208211 if (compatibilityType == null ) {
209212 if (!("sourceCompatibility" .equals (variable .getSimpleName ()) || "targetCompatibility" .equals (variable .getSimpleName ()))) {
@@ -246,13 +249,7 @@ private boolean shouldUpdateStyle(@Nullable DeclarationStyle currentStyle) {
246249 return declarationStyle != null && declarationStyle != currentStyle ;
247250 }
248251
249- public J .MethodInvocation handleMethodInvocation (J .MethodInvocation m , Cursor c , Class <?> enclosing ) {
250- if ("sourceCompatibility" .equals (m .getSimpleName ())) {
251- c .putMessageOnFirstEnclosing (enclosing , SOURCE_COMPATIBILITY_FOUND , true );
252- }
253- if ("targetCompatibility" .equals (m .getSimpleName ())) {
254- c .putMessageOnFirstEnclosing (enclosing , TARGET_COMPATIBILITY_FOUND , true );
255- }
252+ private J .MethodInvocation handleMethodInvocation (J .MethodInvocation m ) {
256253 if ("jvmToolchain" .equals (m .getSimpleName ()) || isMethodInvocation (m , "JavaLanguageVersion" , "of" )) {
257254 List <Expression > args = m .getArguments ();
258255
0 commit comments