2020import org .openrewrite .Preconditions ;
2121import org .openrewrite .Recipe ;
2222import org .openrewrite .TreeVisitor ;
23+ import org .openrewrite .internal .ListUtils ;
2324import org .openrewrite .java .JavaIsoVisitor ;
24- import org .openrewrite .java .JavaParser ;
2525import org .openrewrite .java .JavaTemplate ;
2626import org .openrewrite .java .MethodMatcher ;
27+ import org .openrewrite .java .logging .ArgumentArrayToVarargs ;
2728import org .openrewrite .java .search .UsesMethod ;
2829import org .openrewrite .java .tree .*;
29- import org .openrewrite .marker .Markers ;
3030
31- import java .util .ArrayList ;
32- import java .util .List ;
33- import java .util .Objects ;
31+ import java .util .*;
3432import java .util .regex .Matcher ;
3533import java .util .regex .Pattern ;
3634
3735import static java .util .Collections .emptyList ;
38- import static java .util .Collections . singletonList ;
39- import static org . openrewrite . Tree . randomId ;
36+ import static java .util .Objects . requireNonNull ;
37+ import static java . util . stream . Collectors . toList ;
4038
4139public class JulParameterizedArguments extends Recipe {
4240 private static final MethodMatcher METHOD_MATCHER_PARAM = new MethodMatcher ("java.util.logging.Logger log(java.util.logging.Level, java.lang.String, java.lang.Object)" );
@@ -85,41 +83,56 @@ public static boolean isStringLiteral(Expression expression) {
8583 return null ;
8684 }
8785
88- private static J .Literal buildStringLiteral (String string ) {
89- return new J .Literal (randomId (), Space .EMPTY , Markers .EMPTY , string , String .format ("\" %s\" " , string ), null , JavaType .Primitive .String );
90- }
91-
9286 @ Override
9387 public J .MethodInvocation visitMethodInvocation (J .MethodInvocation method , ExecutionContext ctx ) {
9488 if (METHOD_MATCHER_ARRAY .matches (method ) || METHOD_MATCHER_PARAM .matches (method )) {
9589 List <Expression > originalArguments = method .getArguments ();
9690
9791 Expression levelArgument = originalArguments .get (0 );
9892 Expression messageArgument = originalArguments .get (1 );
93+ Expression stringFormatArgument = originalArguments .get (2 );
9994
10095 if (!(levelArgument instanceof J .FieldAccess || levelArgument instanceof J .Identifier ) ||
101- !isStringLiteral (messageArgument )) {
96+ !isStringLiteral (messageArgument )) {
10297 return method ;
10398 }
10499 String newName = getMethodIdentifier (levelArgument );
105- if (newName == null ) {
100+ if (newName == null ) {
106101 return method ;
107102 }
108103 maybeRemoveImport ("java.util.logging.Level" );
109104
110- String originalFormatString = Objects . requireNonNull ((String ) ((J .Literal ) messageArgument ).getValue ());
105+ String originalFormatString = requireNonNull ((String ) ((J .Literal ) messageArgument ).getValue ());
111106 List <Integer > originalIndices = originalLoggedArgumentIndices (originalFormatString );
112- List <Expression > originalParameters = originalParameters (originalArguments .get (2 ));
113-
114- List <Expression > targetArguments = new ArrayList <>(2 );
115- targetArguments .add (buildStringLiteral (originalFormatString .replaceAll ("\\ {\\ d*}" , "{}" )));
116- originalIndices .forEach (i -> targetArguments .add (originalParameters .get (i )));
117- return JavaTemplate .builder (createTemplateString (newName , targetArguments ))
118- .contextSensitive ()
119- .javaParser (JavaParser .fromJavaVersion ()
120- .classpathFromResources (ctx , "slf4j-api-2.1.+" ))
107+
108+ Expression updatedStringFormatArgument = stringFormatArgument ;
109+ if (stringFormatArgument instanceof J .NewArray ) {
110+ J .NewArray newArray = (J .NewArray ) stringFormatArgument ;
111+ List <Expression > arrayContent = newArray .getInitializer () == null ? emptyList () : newArray .getInitializer ();
112+ updatedStringFormatArgument = newArray
113+ .withInitializer (originalIndices .stream ().map (arrayContent ::get ).collect (toList ()))
114+ // Also unpack `new String[]{ ... }`, as `ArgumentArrayToVarargs` requires `Object[]`
115+ .withType (((JavaType .Array ) requireNonNull (newArray .getType ())).withElemType (JavaType .ShallowClass .build ("java.lang.Object" )));
116+ }
117+
118+ J .MethodInvocation updatedMi = JavaTemplate .builder (newName + "(\" #{}\" ,#{anyArray(Object)})" )
121119 .build ()
122- .apply (getCursor (), method .getCoordinates ().replaceMethod (), targetArguments .toArray ());
120+ .apply (
121+ getCursor (),
122+ method .getCoordinates ().replaceMethod (),
123+ originalFormatString .replaceAll ("\\ {\\ d*}" , "{}" ),
124+ updatedStringFormatArgument
125+ );
126+
127+ // In case of logger.log(Level.INFO, "Hello {0}, {0}", "foo")
128+ if (!(stringFormatArgument instanceof J .NewArray ) && originalIndices .size () > 1 ) {
129+ return updatedMi .withArguments (ListUtils .concatAll (updatedMi .getArguments (), Collections .nCopies (originalIndices .size () - 1 , updatedStringFormatArgument )));
130+ }
131+ // Delegate to ArgumentArrayToVarargs to convert the array argument to varargs
132+ doAfterVisit (new ArgumentArrayToVarargs ().getVisitor ());
133+ Set <Flag > flags = new HashSet <>(requireNonNull (updatedMi .getMethodType ()).getFlags ());
134+ flags .add (Flag .Varargs );
135+ return updatedMi .withMethodType (updatedMi .getMethodType ().withFlags (flags ));
123136 }
124137 return super .visitMethodInvocation (method , ctx );
125138 }
@@ -133,22 +146,5 @@ private List<Integer> originalLoggedArgumentIndices(String strFormat) {
133146 }
134147 return loggedArgumentIndices ;
135148 }
136-
137- private static List <Expression > originalParameters (Expression logParameters ) {
138- if (logParameters instanceof J .NewArray ) {
139- final List <Expression > initializer = ((J .NewArray ) logParameters ).getInitializer ();
140- if (initializer == null || initializer .isEmpty ()) {
141- return emptyList ();
142- }
143- return initializer ;
144- }
145- return singletonList (logParameters );
146- }
147-
148- private static String createTemplateString (String newName , List <Expression > targetArguments ) {
149- List <String > targetArgumentsStrings = new ArrayList <>();
150- targetArguments .forEach (targetArgument -> targetArgumentsStrings .add ("#{any()}" ));
151- return newName + '(' + String .join ("," , targetArgumentsStrings ) + ')' ;
152- }
153149 }
154150}
0 commit comments