1616package org .openrewrite .java .migrate .guava ;
1717
1818import org .jspecify .annotations .Nullable ;
19- import org .openrewrite .ExecutionContext ;
20- import org .openrewrite .Preconditions ;
21- import org .openrewrite .Recipe ;
22- import org .openrewrite .TreeVisitor ;
19+ import org .openrewrite .*;
20+ import org .openrewrite .internal .ListUtils ;
2321import org .openrewrite .java .JavaTemplate ;
2422import org .openrewrite .java .JavaVisitor ;
2523import org .openrewrite .java .MethodMatcher ;
2624import org .openrewrite .java .search .UsesJavaVersion ;
2725import org .openrewrite .java .search .UsesType ;
2826import org .openrewrite .java .tree .*;
27+ import org .openrewrite .marker .Markers ;
2928
3029import java .time .Duration ;
30+ import java .util .ArrayList ;
31+ import java .util .List ;
32+
33+ import static java .util .Collections .emptyList ;
3134
3235abstract class AbstractNoGuavaImmutableOf extends Recipe {
3336
@@ -66,34 +69,86 @@ public TreeVisitor<?, ExecutionContext> getVisitor() {
6669 return Preconditions .check (check , new JavaVisitor <ExecutionContext >() {
6770 @ Override
6871 public J visitMethodInvocation (J .MethodInvocation method , ExecutionContext ctx ) {
69- if (IMMUTABLE_MATCHER .matches (method ) && isParentTypeDownCast (method )) {
70- maybeRemoveImport (guavaType );
71- maybeAddImport (javaType );
72-
73- String template ;
74- Object [] args ;
75- if (method .getArguments ().isEmpty () || method .getArguments ().get (0 ) instanceof J .Empty ) {
76- template = getShortType (javaType ) + ".of()" ;
77- args = new Object []{};
78- } else if ("com.google.common.collect.ImmutableMap" .equals (guavaType )) {
79- template = getShortType (javaType ) + ".of(#{any()}, #{any()})" ;
80- args = new Object []{method .getArguments ().get (0 ), method .getArguments ().get (1 )};
81- } else {
82- template = getShortType (javaType ) + ".of(#{any()})" ;
83- args = new Object []{method .getArguments ().get (0 )};
84- }
72+ J .MethodInvocation mi = (J .MethodInvocation ) super .visitMethodInvocation (method , ctx );
73+ if (!IMMUTABLE_MATCHER .matches (mi ) || !isParentTypeDownCast (mi )) {
74+ return mi ;
75+ }
76+ maybeRemoveImport (guavaType );
77+ maybeAddImport (javaType );
78+
79+ String template ;
80+ Object [] templateArguments ;
81+ List <Expression > methodArguments = mi .getArguments ();
82+ if (methodArguments .isEmpty () || methodArguments .get (0 ) instanceof J .Empty ) {
83+ template = getShortType (javaType ) + ".of()" ;
84+ templateArguments = new Object []{};
85+ } else if ("com.google.common.collect.ImmutableMap" .equals (guavaType )) {
86+ template = getShortType (javaType ) + ".of(#{any()}, #{any()})" ;
87+ templateArguments = new Object []{methodArguments .get (0 ), methodArguments .get (1 )};
88+ } else {
89+ template = getShortType (javaType ) + ".of(#{any()})" ;
90+ templateArguments = new Object []{methodArguments .get (0 )};
91+ }
92+
93+ J .MethodInvocation m = JavaTemplate .builder (template )
94+ .imports (javaType )
95+ .build ()
96+ .apply (getCursor (), mi .getCoordinates ().replace (), templateArguments );
97+ m = m .getPadding ().withArguments (mi .getPadding ().getArguments ());
98+ JavaType .Method newType = (JavaType .Method ) visitType (mi .getMethodType (), ctx );
99+ m = m .withMethodType (newType ).withName (m .getName ().withType (newType ));
100+ return super .visitMethodInvocation (m , ctx );
101+ }
102+
103+ @ Override
104+ public J .VariableDeclarations visitVariableDeclarations (J .VariableDeclarations multiVariable , ExecutionContext ctx ) {
105+ J .VariableDeclarations mv = (J .VariableDeclarations ) super .visitVariableDeclarations (multiVariable , ctx );
106+
107+ if (multiVariable != mv && TypeUtils .isOfClassType (mv .getType (), guavaType )) {
108+ JavaType newType = JavaType .buildType (javaType );
109+ mv = mv .withTypeExpression (mv .getTypeExpression () == null ?
110+ null : createNewTypeExpression (mv .getTypeExpression (), newType ));
85111
86- J . MethodInvocation templated = JavaTemplate . builder ( template )
87- . imports ( javaType )
88- . build ()
89- . apply ( getCursor (),
90- method . getCoordinates (). replace (),
91- args ) ;
92- return templated . getPadding (). withArguments ( method . getPadding (). getArguments ( ));
112+ mv = mv . withVariables ( ListUtils . map ( mv . getVariables (), variable -> {
113+ JavaType . FullyQualified varType = TypeUtils . asFullyQualified ( variable . getType ());
114+ if ( varType != null && ! varType . equals ( newType )) {
115+ return variable . withType ( newType ). withName ( variable . getName (). withType ( newType ));
116+ }
117+ return variable ;
118+ } ));
93119 }
94- return super .visitMethodInvocation (method , ctx );
120+
121+ return mv ;
95122 }
96123
124+ private TypeTree createNewTypeExpression (TypeTree typeTree , JavaType newType ) {
125+ if (typeTree instanceof J .ParameterizedType ) {
126+ J .ParameterizedType parameterizedType = (J .ParameterizedType ) typeTree ;
127+ List <JRightPadded <Expression >> jRightPaddedList = new ArrayList <>();
128+ parameterizedType .getTypeParameters ().forEach (
129+ expression -> {
130+ if (expression instanceof J .ParameterizedType && TypeUtils .isOfClassType (expression .getType (), guavaType )) {
131+ jRightPaddedList .add (JRightPadded .build (((J .ParameterizedType ) createNewTypeExpression ((TypeTree ) expression , newType ))));
132+ } else {
133+ jRightPaddedList .add (JRightPadded .build (expression ));
134+ }
135+ });
136+ NameTree clazz = new J .Identifier (
137+ Tree .randomId (), Space .EMPTY , Markers .EMPTY , emptyList (), getShortType (javaType ), null , null );
138+ return parameterizedType .withClazz (clazz ).withType (newType ).getPadding ().withTypeParameters (JContainer .build (jRightPaddedList ));
139+ }
140+ return new J .Identifier (
141+ typeTree .getId (),
142+ typeTree .getPrefix (),
143+ Markers .EMPTY ,
144+ emptyList (),
145+ getShortType (javaType ),
146+ newType ,
147+ null
148+ );
149+ }
150+
151+
97152 private boolean isParentTypeDownCast (MethodCall immutableMethod ) {
98153 J parent = getCursor ().dropParentUntil (J .class ::isInstance ).getValue ();
99154 boolean isParentTypeDownCast = false ;
@@ -118,8 +173,10 @@ private boolean isParentTypeDownCast(MethodCall immutableMethod) {
118173 } else if (parent instanceof J .MethodInvocation ) {
119174 J .MethodInvocation m = (J .MethodInvocation ) parent ;
120175 int index = m .getArguments ().indexOf (immutableMethod );
121- if (m .getMethodType () != null && index != -1 ) {
176+ if (m .getMethodType () != null && index != -1 && ! m . getMethodType (). getParameterTypes (). isEmpty () ) {
122177 isParentTypeDownCast = isParentTypeMatched (m .getMethodType ().getParameterTypes ().get (index ));
178+ } else {
179+ isParentTypeDownCast = true ;
123180 }
124181 } else if (parent instanceof J .NewClass ) {
125182 J .NewClass c = (J .NewClass ) parent ;
@@ -150,7 +207,8 @@ private boolean isParentTypeDownCast(MethodCall immutableMethod) {
150207 private boolean isParentTypeMatched (@ Nullable JavaType type ) {
151208 JavaType .FullyQualified fq = TypeUtils .asFullyQualified (type );
152209 return TypeUtils .isOfClassType (fq , javaType ) ||
153- TypeUtils .isOfClassType (fq , "java.lang.Object" );
210+ TypeUtils .isOfClassType (fq , "java.lang.Object" ) ||
211+ TypeUtils .isOfClassType (fq , guavaType );
154212 }
155213 });
156214 }
0 commit comments