2626import org .openrewrite .java .tree .TypeUtils ;
2727import org .openrewrite .staticanalysis .VariableReferences ;
2828
29+ import java .util .List ;
30+ import java .util .concurrent .atomic .AtomicBoolean ;
31+ import java .util .stream .Collectors ;
32+
2933import static java .util .Collections .emptyList ;
3034
3135public class MigrateMainMethodToInstanceMain extends Recipe {
@@ -65,6 +69,27 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, Ex
6569 return md ;
6670 }
6771
72+ // Get the enclosing class
73+ J .ClassDeclaration enclosingClass = getCursor ().firstEnclosing (J .ClassDeclaration .class );
74+ if (enclosingClass == null ) {
75+ return md ;
76+ }
77+
78+ // Check 1: Do not migrate if class has @SpringBootApplication annotation
79+ if (hasSpringBootApplicationAnnotation (enclosingClass )) {
80+ return md ;
81+ }
82+
83+ // Check 2: Do not migrate if class doesn't have a no-arg constructor
84+ if (!hasNoArgConstructor (enclosingClass )) {
85+ return md ;
86+ }
87+
88+ // Check 3: Do not migrate if main method is used as a method reference
89+ if (isMainMethodReferenced (md )) {
90+ return md ;
91+ }
92+
6893 // Remove the parameter if unused
6994 J .Identifier variableName = param .getVariables ().get (0 ).getName ();
7095 if (VariableReferences .findRhsReferences (md .getBody (), variableName ).isEmpty ()) {
@@ -73,7 +98,50 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, Ex
7398 return md .withReturnTypeExpression (md .getReturnTypeExpression ().withPrefix (md .getModifiers ().get (0 ).getPrefix ()))
7499 .withModifiers (emptyList ());
75100 }
101+
102+ private boolean hasSpringBootApplicationAnnotation (J .ClassDeclaration classDecl ) {
103+ return classDecl .getLeadingAnnotations ().stream ()
104+ .anyMatch (ann -> TypeUtils .isOfClassType (ann .getType (), "org.springframework.boot.autoconfigure.SpringBootApplication" ));
105+ }
106+
107+ private boolean hasNoArgConstructor (J .ClassDeclaration classDecl ) {
108+ List <J .MethodDeclaration > constructors = classDecl .getBody ().getStatements ().stream ()
109+ .filter (stmt -> stmt instanceof J .MethodDeclaration )
110+ .map (stmt -> (J .MethodDeclaration ) stmt )
111+ .filter (J .MethodDeclaration ::isConstructor )
112+ .collect (Collectors .toList ());
113+
114+ // If no constructors are declared, the class has an implicit no-arg constructor
115+ if (constructors .isEmpty ()) {
116+ return true ;
117+ }
118+
119+ // Check if any explicit constructor is a no-arg constructor
120+ return constructors .stream ()
121+ .anyMatch (ctor -> ctor .getParameters ().isEmpty () ||
122+ (ctor .getParameters ().size () == 1 && ctor .getParameters ().get (0 ) instanceof J .Empty ));
123+ }
124+
125+ private boolean isMainMethodReferenced (J .MethodDeclaration mainMethod ) {
126+ J .CompilationUnit cu = getCursor ().firstEnclosing (J .CompilationUnit .class );
127+ if (cu == null ) {
128+ return false ;
129+ }
130+
131+ // Search for method references to main
132+ return new JavaIsoVisitor <AtomicBoolean >(){
133+ @ Override
134+ public J .MemberReference visitMemberReference (J .MemberReference memberRef , AtomicBoolean referenced ) {
135+ // Check if this is a reference to the main method
136+ if ("main" .equals (memberRef .getReference ().getSimpleName ()) &&
137+ memberRef .getMethodType () != null &&
138+ TypeUtils .isOfType (memberRef .getMethodType (), mainMethod .getMethodType ())) {
139+ referenced .set (true );
140+ }
141+ return super .visitMemberReference (memberRef , referenced );
142+ }
143+ }.reduce (cu , new AtomicBoolean ()).get ();
144+ }
76145 });
77146 }
78-
79147}
0 commit comments