1616package org .openrewrite .hibernate ;
1717
1818import lombok .Getter ;
19+ import org .jspecify .annotations .Nullable ;
1920import org .openrewrite .ExecutionContext ;
2021import org .openrewrite .Preconditions ;
2122import org .openrewrite .Recipe ;
2627import org .openrewrite .java .search .UsesType ;
2728import org .openrewrite .java .tree .Expression ;
2829import org .openrewrite .java .tree .J ;
30+ import org .openrewrite .java .tree .JavaType ;
2931import org .openrewrite .java .tree .TypeUtils ;
3032
3133import java .util .HashMap ;
@@ -37,12 +39,17 @@ public class MigrateBooleanMappings extends Recipe {
3739 private static final Map <String , String > REPLACEMENTS = new HashMap <>();
3840
3941 static {
40- REPLACEMENTS . put ( "org.hibernate.type.TrueFalseBooleanType" , "TrueFalseConverter" );
42+ // true/false boolean
4143 REPLACEMENTS .put ("true_false" , "TrueFalseConverter" );
42- REPLACEMENTS .put ("org.hibernate.type.YesNoBooleanType" , "YesNoConverter" );
44+ REPLACEMENTS .put ("org.hibernate.type.TrueFalseType" , "TrueFalseConverter" );
45+ REPLACEMENTS .put ("org.hibernate.type.TrueFalseBooleanType" , "TrueFalseConverter" );
46+ // yes/no boolean
4347 REPLACEMENTS .put ("yes_no" , "YesNoConverter" );
44- REPLACEMENTS .put ("org.hibernate.type.NumericBooleanType" , "NumericBooleanConverter" );
48+ REPLACEMENTS .put ("org.hibernate.type.YesNoType" , "YesNoConverter" );
49+ REPLACEMENTS .put ("org.hibernate.type.YesNoBooleanType" , "YesNoConverter" );
50+ // numeric boolean
4551 REPLACEMENTS .put ("numeric_boolean" , "NumericBooleanConverter" );
52+ REPLACEMENTS .put ("org.hibernate.type.NumericBooleanType" , "NumericBooleanConverter" );
4653 }
4754
4855 @ Getter
@@ -61,47 +68,74 @@ public J.Annotation visitAnnotation(J.Annotation annotation, ExecutionContext ct
6168 if (!TypeUtils .isOfClassType (ann .getType (), "org.hibernate.annotations.Type" )) {
6269 return ann ;
6370 }
64-
6571 List <Expression > args = ann .getArguments ();
6672 if (args == null ) {
6773 return ann ;
6874 }
6975
70- Object type = args .stream ()
71- .filter (exp -> {
72- if (exp instanceof J .Assignment ) {
73- J .Identifier variable = (J .Identifier ) ((J .Assignment ) exp ).getVariable ();
74- return "type" .equals (variable .getSimpleName ());
75- }
76- return false ;
77- })
78- .findFirst ()
79- .map (exp -> {
80- Expression value = ((J .Assignment ) exp ).getAssignment ();
81- if (value instanceof J .Literal ) {
82- return ((J .Literal ) value ).getValue ();
83- }
84- return null ;
85- })
86- .orElse (null );
87-
88- if (type instanceof String && REPLACEMENTS .containsKey ((String ) type )) {
89- String converterName = REPLACEMENTS .get ((String ) type );
90- String converterFQN = String .format ("org.hibernate.type.%s" , converterName );
76+ String converterName = null ;
77+ String legacyClassFQN = null ;
78+ for (Expression arg : args ) {
79+ String key ;
80+ if (arg instanceof J .Assignment ) {
81+ J .Assignment a = (J .Assignment ) arg ;
82+ if (!(a .getVariable () instanceof J .Identifier )) {
83+ continue ;
84+ }
85+ String attr = ((J .Identifier ) a .getVariable ()).getSimpleName ();
86+ if ("type" .equals (attr ) && a .getAssignment () instanceof J .Literal ) {
87+ Object v = ((J .Literal ) a .getAssignment ()).getValue ();
88+ key = v instanceof String ? (String ) v : null ;
89+ } else if ("value" .equals (attr )) {
90+ legacyClassFQN = classRefFQN (a .getAssignment ());
91+ key = legacyClassFQN ;
92+ } else {
93+ continue ;
94+ }
95+ } else if (args .size () == 1 ) {
96+ legacyClassFQN = classRefFQN (arg );
97+ key = legacyClassFQN ;
98+ } else {
99+ continue ;
100+ }
101+ if (key != null ) {
102+ converterName = REPLACEMENTS .get (key );
103+ if (converterName != null ) {
104+ break ;
105+ }
106+ }
107+ }
108+ if (converterName == null ) {
109+ return ann ;
110+ }
91111
92- ann = JavaTemplate .builder (String .format ("@Convert(converter = %s.class)" , converterName ))
93- .javaParser (JavaParser .fromJavaVersion ().classpathFromResources (ctx , "hibernate-core-6+" , "jakarta.persistence-api" ))
94- .imports (converterFQN , "jakarta.persistence.Convert" )
95- .contextSensitive ()
96- .build ().apply (getCursor (), ann .getCoordinates ().replace ());
112+ String converterFQN = "org.hibernate.type." + converterName ;
113+ ann = JavaTemplate .builder ("@Convert(converter = " + converterName + ".class)" )
114+ .javaParser (JavaParser .fromJavaVersion ().classpathFromResources (ctx , "hibernate-core-6+" , "jakarta.persistence-api" ))
115+ .imports (converterFQN , "jakarta.persistence.Convert" )
116+ .contextSensitive ()
117+ .build ().apply (getCursor (), ann .getCoordinates ().replace ());
97118
98- maybeRemoveImport ("org.hibernate.annotations.Type" );
99- maybeAddImport ( "jakarta.persistence.Convert" );
100- maybeAddImport ( converterFQN );
119+ maybeRemoveImport ("org.hibernate.annotations.Type" );
120+ if ( legacyClassFQN != null ) {
121+ maybeRemoveImport ( legacyClassFQN );
101122 }
102-
123+ maybeAddImport ("jakarta.persistence.Convert" );
124+ maybeAddImport (converterFQN );
103125 return ann ;
104126 }
127+
128+ private @ Nullable String classRefFQN (Expression expr ) {
129+ if (!(expr instanceof J .FieldAccess )) {
130+ return null ;
131+ }
132+ J .FieldAccess fa = (J .FieldAccess ) expr ;
133+ if (!"class" .equals (fa .getName ().getSimpleName ())) {
134+ return null ;
135+ }
136+ JavaType .FullyQualified fqn = TypeUtils .asFullyQualified (fa .getTarget ().getType ());
137+ return fqn == null ? null : fqn .getFullyQualifiedName ();
138+ }
105139 }
106140 );
107141 }
0 commit comments