1+ /*
2+ * Copyright 2025 the original author or authors.
3+ * <p>
4+ * Licensed under the Moderne Source Available License (the "License");
5+ * you may not use this file except in compliance with the License.
6+ * You may obtain a copy of the License at
7+ * <p>
8+ * https://docs.moderne.io/licensing/moderne-source-available-license
9+ * <p>
10+ * Unless required by applicable law or agreed to in writing, software
11+ * distributed under the License is distributed on an "AS IS" BASIS,
12+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ * See the License for the specific language governing permissions and
14+ * limitations under the License.
15+ */
116package org .openrewrite .java .testing .junit5 ;
217
318import org .jspecify .annotations .Nullable ;
419import org .openrewrite .*;
20+ import org .openrewrite .internal .ListUtils ;
521import org .openrewrite .java .*;
622import org .openrewrite .java .format .AutoFormatVisitor ;
723import org .openrewrite .java .search .UsesType ;
8- import org .openrewrite .java .tree .J ;
9- import org .openrewrite .java .tree .TypeUtils ;
24+ import org .openrewrite .java .style .IntelliJ ;
25+ import org .openrewrite .java .style .WrappingAndBracesStyle ;
26+ import org .openrewrite .java .tree .*;
27+ import org .openrewrite .style .LineWrapSetting ;
28+ import org .openrewrite .style .NamedStyles ;
29+ import org .openrewrite .style .Style ;
1030
11- import java .util .HashMap ;
12- import java .util .List ;
13- import java .util .Map ;
14- import java .util .UUID ;
31+ import java .util .*;
1532import java .util .stream .IntStream ;
33+ import java .util .stream .Stream ;
1634
1735import static java .util .Collections .emptyList ;
36+ import static java .util .Collections .emptySet ;
1837import static java .util .stream .Collectors .toList ;
38+ import static org .openrewrite .java .tree .JavaType .ShallowClass .build ;
1939
2040public class UpdateMockWebServerMockResponse extends Recipe {
2141 private static final String OLD_MOCKRESPONSE_FQN = "okhttp3.mockwebserver.MockResponse" ;
@@ -25,6 +45,8 @@ public class UpdateMockWebServerMockResponse extends Recipe {
2545 private static final String OLD_MOCKRESPONSE_SETHEADERS = OLD_MOCKRESPONSE_FQN + " setHeaders(okhttp3.Headers)" ;
2646 private static final String NEW_MOCKRESPONSE_FQN = "mockwebserver3.MockResponse" ;
2747 private static final String NEW_MOCKRESPONSE_BUILDER_FQN = NEW_MOCKRESPONSE_FQN + "$Builder" ;
48+ private static final String OLD_MOCKWEBSERVER_FQN = "okhttp3.mockwebserver.MockWebServer" ;
49+ private static final String NEW_MOCKWEBSERVER_FQN = "mockwebserver3.MockWebServer" ;
2850
2951 @ Override
3052 public String getDisplayName () {
@@ -40,7 +62,6 @@ public String getDescription() {
4062 public TreeVisitor <?, ExecutionContext > getVisitor () {
4163 return Preconditions .check (new UsesType <>(OLD_MOCKRESPONSE_FQN , false ), new JavaIsoVisitor <ExecutionContext >() {
4264 private final Map <UUID , List <Integer >> methodInvocationsToAdjust = new HashMap <>();
43-
4465 @ Override
4566 public @ Nullable J visit (@ Nullable Tree tree , ExecutionContext ctx ) {
4667 J j = (J ) tree ;
@@ -51,8 +72,8 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation methodInv, Ex
5172 List <Integer > indexes = IntStream .range (0 , mi .getArguments ().size ())
5273 .filter (x -> TypeUtils .isAssignableTo (OLD_MOCKRESPONSE_FQN , mi .getArguments ().get (x ).getType ()))
5374 .boxed ().collect (toList ());
54- if (!indexes .isEmpty () && ! methodInvocationsToAdjust . containsKey ( mi . getId ()) ) {
55- methodInvocationsToAdjust .put (mi .getId (), indexes );
75+ if (!indexes .isEmpty ()) {
76+ methodInvocationsToAdjust .putIfAbsent (mi .getId (), indexes );
5677 }
5778 return mi ;
5879 }
@@ -82,14 +103,24 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation methodInv, Ex
82103 NEW_MOCKRESPONSE_BUILDER_FQN ,
83104 null
84105 ).getVisitor ().visit (j , ctx );
85- j = new JavaIsoVisitor <ExecutionContext >() {
106+ // j = (J) new ChangeType(
107+ // OLD_MOCKRESPONSE_FQN,
108+ // NEW_MOCKRESPONSE_FQN,
109+ // null
110+ // ).getVisitor().visit(j, ctx);
111+ j = (J ) new ChangeType (
112+ OLD_MOCKWEBSERVER_FQN ,
113+ NEW_MOCKWEBSERVER_FQN ,
114+ null
115+ ).getVisitor ().visit (j , ctx );
116+ return new JavaIsoVisitor <ExecutionContext >() {
86117 @ Override
87118 public J .MethodInvocation visitMethodInvocation (J .MethodInvocation methodInv , ExecutionContext ctx ) {
88119 J .MethodInvocation mi = super .visitMethodInvocation (methodInv , ctx );
89- List <Integer > indexes = methodInvocationsToAdjust .getOrDefault (mi .getId (), emptyList ());
90- if (!indexes .isEmpty ()) {
91- methodInvocationsToAdjust .remove (mi .getId ());
120+ List <Integer > indexes = methodInvocationsToAdjust .remove (mi .getId ());
121+ if (indexes != null ) {
92122 StringBuilder sb = new StringBuilder ();
123+ List <JRightPadded <Expression >> oldArgs = mi .getPadding ().getArguments ().getPadding ().getElements ();
93124 for (int i = 0 ; i < mi .getArguments ().size (); i ++) {
94125 sb .append ("#{any()}" );
95126 if (indexes .contains (i )) {
@@ -99,23 +130,85 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation methodInv, Ex
99130 sb .append (", " );
100131 }
101132 }
102- AutoFormatVisitor <Object > formatVisitor = new AutoFormatVisitor <>(null , true );
103- return (J .MethodInvocation ) formatVisitor .visit (
133+ Style s1 = IntelliJ .tabsAndIndents ().withContinuationIndent (4 );
134+ WrappingAndBracesStyle s2 = IntelliJ .wrappingAndBraces ();
135+ s2 = s2 .withChainedMethodCalls (
136+ s2 .getChainedMethodCalls ().withWrap (LineWrapSetting .WrapAlways )
137+ );
138+ AutoFormatVisitor <Object > formatVisitor = new AutoFormatVisitor <>(
139+ null ,
140+ false ,
141+ new NamedStyles (
142+ Tree .randomId (),
143+ "test" ,
144+ "test" ,
145+ "test" ,
146+ emptySet (),
147+ Arrays .asList (s1 , s2 )
148+ )
149+ );
150+ J .MethodInvocation mi3 = (J .MethodInvocation ) formatVisitor .visit (
104151 JavaTemplate
105152 .builder (sb .toString ())
106153 .contextSensitive ()
107154 .javaParser (JavaParser .fromJavaVersion ().classpathFromResources (ctx , "mockwebserver3" ))
155+ .imports ("mockwebserver3.MockResponse" , "mockwebserver3.MockResponse.Builder" , "mockwebserver3.MockWebServer" )
108156 .build ()
109157 .apply (getCursor (), mi .getCoordinates ().replaceArguments (), mi .getArguments ().toArray ()),
110158 ctx ,
111159 getCursor ().getParent ()
112160 );
161+ // TODO: Cleanup backpatching of parameter types for method invocations
162+ mi3 = mi3 .withMethodType (mi .getMethodType ().withParameterTypes (ListUtils .map (mi .getMethodType ().getParameterTypes (), (index , x ) -> {
163+ if (indexes .contains (index )) {
164+ return JavaType .buildType (NEW_MOCKRESPONSE_FQN );
165+ }
166+ return x ;
167+ })));
168+ mi3 = mi3 .getPadding ().withArguments (mi3 .getPadding ().getArguments ().getPadding ().withElements (ListUtils .map (mi3 .getPadding ().getArguments ().getPadding ().getElements (), (index , x ) -> {
169+ JRightPadded <Expression > oldArg = oldArgs .get (index );
170+ if (indexes .contains (index )) {
171+ // TODO: Cleanup backpatching of parameter types for method invocations
172+ x = x .withElement (((J .MethodInvocation ) x .getElement ()).withMethodType (newMethodType (NEW_MOCKRESPONSE_BUILDER_FQN , NEW_MOCKRESPONSE_FQN , "build" )));
173+ }
174+ // Below is backpatching prefixes and afters to restore formatting prior to chaining `.build()`
175+ return x
176+ .withAfter (oldArg .getAfter ())
177+ .withElement (x .getElement ().withPrefix (oldArg .getElement ().getPrefix ()));
178+ })));
179+ // TODO: Cleanup backpatching of parameter types for method invocation's name's type
180+ mi3 = mi3 .withName (mi3 .getName ().withType (((JavaType .Method ) mi3 .getName ().getType ()).withParameterTypes (mi3 .getPadding ().getArguments ().getElements ().stream ().map (z -> z .getType ()).collect (toList ()))));
181+ return mi3 ;
113182 }
114183 return mi ;
115184 }
116185 }.visit (j , ctx );
117- return j ;
118186 }
119187 });
120188 }
189+
190+ // TODO: figure out a nicer way of doing this potentially. This is taken from MethodMatcherTest in openrewrite/rewrite and altered slightly
191+ private static JavaType .Method newMethodType (String declaringType , @ Nullable String returnType , String method , String ... parameterTypes ) {
192+ List <JavaType > parameterTypeList = Stream .of (parameterTypes )
193+ .map (name -> {
194+ JavaType .Primitive primitive = JavaType .Primitive .fromKeyword (name );
195+ return primitive != null ? primitive : JavaType .ShallowClass .build (name );
196+ })
197+ .map (JavaType .class ::cast )
198+ .collect (toList ());
199+
200+ return new JavaType .Method (
201+ null ,
202+ 1L ,
203+ build (declaringType ),
204+ method ,
205+ returnType == null ? null : build (returnType ),
206+ null ,
207+ parameterTypeList ,
208+ emptyList (),
209+ emptyList (),
210+ emptyList (),
211+ null
212+ );
213+ }
121214}
0 commit comments