77
88import java .util .Collections ;
99import java .util .List ;
10+ import java .util .Set ;
11+ import java .util .SortedSet ;
12+ import java .util .TreeSet ;
1013import java .util .regex .Pattern ;
14+ import java .util .stream .Collectors ;
1115
1216import com .intellij .codeInsight .AnnotationUtil ;
1317import com .intellij .lang .injection .MultiHostInjector ;
2024import com .intellij .psi .PsiAnnotationMemberValue ;
2125import com .intellij .psi .PsiAnnotationParameterList ;
2226import com .intellij .psi .PsiClass ;
27+ import com .intellij .psi .PsiClassObjectAccessExpression ;
2328import com .intellij .psi .PsiClassType ;
2429import com .intellij .psi .PsiElement ;
2530import com .intellij .psi .PsiJavaCodeReferenceElement ;
3035import com .intellij .psi .PsiParameter ;
3136import com .intellij .psi .PsiReference ;
3237import com .intellij .psi .PsiType ;
38+ import com .intellij .psi .PsiTypeParameter ;
3339import com .intellij .psi .impl .source .resolve .reference .ReferenceProvidersRegistry ;
3440import com .intellij .psi .util .PsiTreeUtil ;
3541import com .intellij .psi .util .PsiUtil ;
@@ -50,14 +56,102 @@ public class JavaExpressionInjector implements MultiHostInjector {
5056 MapstructElementUtils .mappingElementPattern ( "defaultExpression" )
5157 );
5258
59+ private void importIfNecessary (PsiClass cls , @ NotNull Set <String > imports ) {
60+ if ( cls != null
61+ && cls .getQualifiedName () != null
62+ && !cls .getQualifiedName ().startsWith ( "java.lang." )
63+ ) {
64+ imports .add ( cls .getQualifiedName () );
65+ }
66+ }
67+
68+ private void appendType (@ NotNull StringBuilder sb , @ NotNull Set <String > imports , @ NotNull PsiType type ) {
69+ importIfNecessary ( PsiUtil .resolveClassInType ( type ), imports );
70+ if ( !( type instanceof PsiClassType ) ) {
71+ sb .append ( type .getPresentableText () );
72+ return ;
73+ }
74+ PsiClassType ct = (PsiClassType ) type ;
75+ sb .append ( ct .getName () );
76+ PsiType [] typeParameters = ct .getParameters ();
77+ if ( typeParameters .length == 0 ) {
78+ return ;
79+ }
80+ sb .append ( '<' );
81+ for ( int i = 0 ; i < typeParameters .length ; ++i ) {
82+ if ( i != 0 ) {
83+ sb .append ( ", " );
84+ }
85+ appendType ( sb , imports , typeParameters [i ] );
86+ }
87+ sb .append ( '>' );
88+ }
89+
90+ private void appendClassSimple (@ NotNull StringBuilder sb , @ NotNull Set <String > imports , @ NotNull PsiClass cls ) {
91+ importIfNecessary ( cls , imports );
92+ sb .append ( cls .getName () );
93+ PsiTypeParameter [] typeParameters = cls .getTypeParameters ();
94+ if ( typeParameters .length == 0 ) {
95+ return ;
96+ }
97+ sb .append ( '<' );
98+ for ( int i = 0 ; i < typeParameters .length ; ++i ) {
99+ if ( i != 0 ) {
100+ sb .append ( ", " );
101+ }
102+ appendClassSimple ( sb , imports , typeParameters [i ] );
103+ }
104+ sb .append ( '>' );
105+ }
106+
107+ private void appendClassImpl (@ NotNull StringBuilder sb , @ NotNull Set <String > imports , @ NotNull PsiClass cls ) {
108+ importIfNecessary ( cls , imports );
109+ sb .append ( cls .getName () ).append ( "Impl" );
110+ appendTypeParametersHard ( sb , imports , cls .getTypeParameters () );
111+ }
112+
113+ private boolean appendTypeParametersHard (
114+ @ NotNull StringBuilder sb , @ NotNull Set <String > imports , PsiTypeParameter [] typeParameters
115+ ) {
116+ if ( typeParameters .length == 0 ) {
117+ return false ;
118+ }
119+ sb .append ( "<" );
120+ for ( int i = 0 ; i < typeParameters .length ; ++i ) {
121+ if ( i != 0 ) {
122+ sb .append ( ", " );
123+ }
124+ sb .append ( typeParameters [i ].getName () );
125+ PsiClassType [] ext = typeParameters [i ].getExtendsListTypes ();
126+ if ( ext .length == 0 ) {
127+ continue ;
128+ }
129+ sb .append ( " extends " );
130+ for ( int j = 0 ; j < ext .length ; ++j ) {
131+ if ( j != 0 ) {
132+ sb .append ( ", " );
133+ }
134+ appendType ( sb , imports , ext [j ] );
135+ }
136+ }
137+ sb .append ( ">" );
138+ return true ;
139+ }
140+
141+ private void appendNesting (StringBuilder sb , int level ) {
142+ for ( int i = 0 ; i < level ; i ++ ) {
143+ sb .append ( " " );
144+ }
145+ }
146+
53147 @ Override
54148 public void getLanguagesToInject (@ NotNull MultiHostRegistrar registrar , @ NotNull PsiElement context ) {
55149
56150 if ( PATTERN .accepts ( context ) && context instanceof PsiLiteralExpression &&
57151 JAVA_EXPRESSION .matcher ( context .getText () ).matches () ) {
58152
59153 // Context is the PsiLiteralExpression
60- // In order to reach the method have the following steps to do :
154+ // In order to reach the method have the following steps to take :
61155 // PsiLiteralExpression - "java(something)"
62156 // PsiNameValuePair - expression = "java(something)"
63157 // PsiAnnotationParameterList - target = "", expression = "java(something)"
@@ -74,7 +168,7 @@ public void getLanguagesToInject(@NotNull MultiHostRegistrar registrar, @NotNull
74168 }
75169 PsiType targetType = null ;
76170 for ( PsiNameValuePair attribute : annotationParameterList .getAttributes () ) {
77- if ( "target" .equals ( attribute .getAttributeName () ) ) {
171+ if ( "target" .equals ( attribute .getAttributeName () ) ) {
78172 PsiAnnotationMemberValue attributeValue = attribute .getValue ();
79173 if ( attributeValue != null ) {
80174 PsiReference [] references = ReferenceProvidersRegistry .getReferencesFromProviders (
@@ -106,62 +200,65 @@ else if ( resolved instanceof PsiParameter ) {
106200 if ( mapperClass == null ) {
107201 return ;
108202 }
109- StringBuilder importsBuilder = new StringBuilder ();
203+
204+ SortedSet <String > imports = new TreeSet <>();
110205 StringBuilder prefixBuilder = new StringBuilder ();
111206
112- prefixBuilder .append ( "public class " )
113- .append ( mapperClass .getName () ).append ( "Impl" )
114- .append ( " implements " ).append ( mapperClass .getQualifiedName () ).append ( "{ " )
115- .append ( "public " ).append ( method .getReturnType ().getCanonicalText () ).append ( " " )
116- .append ( method .getName () ).append ( "(" );
207+ prefixBuilder .append ( "\n @SuppressWarnings(\" unused\" )" );
208+ prefixBuilder .append ( "\n abstract class " );
209+ appendClassImpl ( prefixBuilder , imports , mapperClass );
210+ prefixBuilder .append ( "\n " );
211+ appendNesting ( prefixBuilder , 1 );
212+ prefixBuilder .append ( mapperClass .isInterface () ? "implements " : "extends " );
213+ appendClassSimple ( prefixBuilder , imports , mapperClass );
214+ prefixBuilder .append ( " {\n \n " );
215+ appendNesting ( prefixBuilder , 1 );
216+ if ( appendTypeParametersHard ( prefixBuilder , imports , method .getTypeParameters () ) ) {
217+ prefixBuilder .append ( " " );
218+ }
219+ prefixBuilder .append ( "void __test__(\n " );
117220
118221 PsiParameter [] parameters = method .getParameterList ().getParameters ();
119222 for ( int i = 0 ; i < parameters .length ; i ++ ) {
120223 if ( i != 0 ) {
121- prefixBuilder .append ( "," );
224+ prefixBuilder .append ( ",\n " );
122225 }
123226
124227 PsiParameter parameter = parameters [i ];
125228 PsiType parameterType = parameter .getType ();
126- PsiClass parameterClass = PsiUtil .resolveClassInType ( parameterType );
127-
128- if ( parameterClass == null ) {
129- return ;
229+ for ( PsiAnnotation a : parameter .getAnnotations () ) {
230+ appendNesting ( prefixBuilder , 2 );
231+ prefixBuilder .append ( a .getText () ).append ( "\n " );
130232 }
131-
132- importsBuilder .append ( "import " ).append ( parameterClass .getQualifiedName () ).append ( ";\n " );
133-
134- prefixBuilder .append ( parameterType .getCanonicalText () ).append ( " " ).append ( parameter .getName () );
233+ appendNesting ( prefixBuilder , 2 );
234+ appendType ( prefixBuilder , imports , parameterType );
235+ prefixBuilder .append ( " " ).append ( parameter .getName () );
135236 }
136237
137- prefixBuilder .append ( ") {" )
138- .append ( targetType .getCanonicalText () ).append ( " __target__ =" );
139-
140-
141- PsiClass targetClass = PsiUtil .resolveClassInType ( targetType );
142- if ( targetClass != null ) {
143- importsBuilder .append ( "import " ).append ( targetClass .getQualifiedName () ).append ( ";\n " );
144- }
145- if ( targetType instanceof PsiClassType ) {
146- for ( PsiType typeParameter : ( (PsiClassType ) targetType ).getParameters () ) {
147- PsiClass typeClass = PsiUtil .resolveClassInType ( typeParameter );
148- if ( typeClass != null ) {
149- importsBuilder .append ( "import " ).append ( typeClass .getQualifiedName () ).append ( ";\n " );
150- }
151- }
152- }
238+ prefixBuilder .append ( "\n " );
239+ appendNesting ( prefixBuilder , 1 );
240+ prefixBuilder .append ( ") {\n " );
241+ appendNesting ( prefixBuilder , 2 );
242+ appendType ( prefixBuilder , imports , targetType );
243+ prefixBuilder .append ( " __target__ = " );
153244
154245 PsiAnnotation mapper = mapperClass .getAnnotation ( MapstructUtil .MAPPER_ANNOTATION_FQN );
155246 if ( mapper != null ) {
156247 for ( PsiNameValuePair attribute : mapper .getParameterList ().getAttributes () ) {
157- if ( "imports" .equals ( attribute .getName () ) ) {
248+ if ( "imports" .equals ( attribute .getName () ) ) {
158249 for ( PsiAnnotationMemberValue importValue : AnnotationUtil .arrayAttributeValues (
159250 attribute .getValue () ) ) {
160251
161252 if ( importValue instanceof PsiJavaCodeReferenceElement ) {
162- importsBuilder .append ( "import " )
163- .append ( ( (PsiJavaCodeReferenceElement ) importValue ).getQualifiedName () )
164- .append ( ";" );
253+ imports .add ( ( (PsiJavaCodeReferenceElement ) importValue ).getQualifiedName () );
254+ }
255+ else if ( importValue instanceof PsiClassObjectAccessExpression ) {
256+ PsiJavaCodeReferenceElement referenceElement =
257+ ( (PsiClassObjectAccessExpression ) importValue ).getOperand ()
258+ .getInnermostComponentReferenceElement ();
259+ if ( referenceElement != null ) {
260+ imports .add ( referenceElement .getQualifiedName () );
261+ }
165262 }
166263 }
167264 }
@@ -170,10 +267,11 @@ else if ( resolved instanceof PsiParameter ) {
170267
171268 registrar .startInjecting ( JavaLanguage .INSTANCE )
172269 .addPlace (
173- importsBuilder .toString () + prefixBuilder .toString (),
174- ";} }" ,
270+ imports .stream ().map ( imp -> "import " + imp + ";" ).collect ( Collectors .joining ( "\n " , "" , "\n " ) )
271+ + prefixBuilder ,
272+ ";\n }\n }" ,
175273 (PsiLanguageInjectionHost ) context ,
176- new TextRange ( 6 , context .getTextRange ().getLength () - 2 )
274+ new TextRange ( " \" java(" . length () , context .getTextRange ().getLength () - ") \" " . length () )
177275 )
178276 .doneInjecting ();
179277 }
0 commit comments