10
10
*******************************************************************************/
11
11
package org .springframework .ide .vscode .boot .java .beans ;
12
12
13
+ import java .util .ArrayList ;
13
14
import java .util .Arrays ;
14
15
import java .util .Collection ;
15
16
import java .util .HashSet ;
18
19
import java .util .stream .Collectors ;
19
20
import java .util .stream .Stream ;
20
21
22
+ import org .eclipse .jdt .core .dom .ASTNode ;
21
23
import org .eclipse .jdt .core .dom .ASTVisitor ;
22
24
import org .eclipse .jdt .core .dom .AbstractTypeDeclaration ;
23
25
import org .eclipse .jdt .core .dom .Annotation ;
26
+ import org .eclipse .jdt .core .dom .Block ;
24
27
import org .eclipse .jdt .core .dom .Expression ;
25
28
import org .eclipse .jdt .core .dom .IMethodBinding ;
26
29
import org .eclipse .jdt .core .dom .ITypeBinding ;
@@ -140,6 +143,7 @@ private void createSymbol(TypeDeclaration type, Annotation node, ITypeBinding an
140
143
indexEventListenerInterfaceImplementation (beanDefinition , type , context , doc );
141
144
indexRequestMappings (beanDefinition , type , annotationType , metaAnnotations , context , doc );
142
145
indexConfigurationProperties (beanDefinition , type , context , doc );
146
+ indexBeanRegistrarImplementation (beanDefinition , type , context , doc );
143
147
144
148
context .getGeneratedSymbols ().add (new CachedSymbol (context .getDocURI (), context .getLastModified (), symbol ));
145
149
context .getBeans ().add (new CachedBean (context .getDocURI (), beanDefinition ));
@@ -321,6 +325,7 @@ public void addSymbols(TypeDeclaration typeDeclaration, SpringIndexerJavaContext
321
325
// check for event listener implementations on classes that are not annotated with component, but created via bean methods (for example)
322
326
if (!isComponment ) {
323
327
indexEventListenerInterfaceImplementation (null , typeDeclaration , context , doc );
328
+ indexBeanRegistrarImplementation (null , typeDeclaration , context , doc );
324
329
}
325
330
326
331
}
@@ -377,6 +382,191 @@ private MethodDeclaration findHandleEventMethod(TypeDeclaration type) {
377
382
return null ;
378
383
}
379
384
385
+ private MethodDeclaration findRegisterMethod (TypeDeclaration type , ITypeBinding beanRegistrarType ) {
386
+ IMethodBinding [] beanRegistrarMethods = beanRegistrarType .getDeclaredMethods ();
387
+ if (beanRegistrarMethods == null || beanRegistrarMethods .length != 1 || !"register" .equals (beanRegistrarMethods [0 ].getName ())) {
388
+ return null ;
389
+ }
390
+
391
+ MethodDeclaration [] methods = type .getMethods ();
392
+
393
+ for (MethodDeclaration method : methods ) {
394
+ IMethodBinding binding = method .resolveBinding ();
395
+ boolean overrides = binding .overrides (beanRegistrarMethods [0 ]);
396
+ if (overrides ) {
397
+ return method ;
398
+ }
399
+ }
400
+
401
+ return null ;
402
+ }
403
+
404
+ private void indexBeanRegistrarImplementation (Bean bean , TypeDeclaration typeDeclaration , SpringIndexerJavaContext context , TextDocument doc ) {
405
+ try {
406
+ ITypeBinding typeBinding = typeDeclaration .resolveBinding ();
407
+ if (typeBinding == null ) return ;
408
+
409
+ ITypeBinding inTypeHierarchy = ASTUtils .findInTypeHierarchy (typeDeclaration , doc , typeBinding , Set .of (Annotations .BEAN_REGISTRAR_INTERFACE ));
410
+ if (inTypeHierarchy == null ) return ;
411
+
412
+ MethodDeclaration registerMethod = findRegisterMethod (typeDeclaration , inTypeHierarchy );
413
+ if (registerMethod == null ) return ;
414
+
415
+ if (!context .isFullAst ()) { // needs full method bodies to continue
416
+ throw new RequiredCompleteAstException ();
417
+ }
418
+
419
+ if (bean == null ) { // need to create and register bean element
420
+ String beanType = typeBinding .getQualifiedName ();
421
+ String beanName = BeanUtils .getBeanNameFromType (typeBinding .getName ());
422
+
423
+ Location location = new Location (doc .getUri (), doc .toRange (typeDeclaration .getStartPosition (), typeDeclaration .getLength ()));
424
+
425
+ WorkspaceSymbol symbol = new WorkspaceSymbol (
426
+ beanLabel ("+" , null , null , beanName , beanType ),
427
+ SymbolKind .Class ,
428
+ Either .forLeft (location ));
429
+
430
+ InjectionPoint [] injectionPoints = ASTUtils .findInjectionPoints (typeDeclaration , doc );
431
+
432
+ Set <String > supertypes = new HashSet <>();
433
+ ASTUtils .findSupertypes (typeBinding , supertypes );
434
+
435
+ Collection <Annotation > annotationsOnMethod = ASTUtils .getAnnotations (typeDeclaration );
436
+ AnnotationMetadata [] annotations = ASTUtils .getAnnotationsMetadata (annotationsOnMethod , doc );
437
+
438
+ bean = new Bean (beanName , beanType , location , injectionPoints , supertypes , annotations , false , symbol .getName ());
439
+
440
+ context .getGeneratedSymbols ().add (new CachedSymbol (context .getDocURI (), context .getLastModified (), symbol ));
441
+ context .getBeans ().add (new CachedBean (context .getDocURI (), bean ));
442
+ }
443
+
444
+ scanBeanRegistryInvocations (bean , registerMethod .getBody (), context , doc );
445
+
446
+ } catch (BadLocationException e ) {
447
+ log .error ("" , e );
448
+ }
449
+ }
450
+
451
+ private void scanBeanRegistryInvocations (Bean component , Block body , SpringIndexerJavaContext context , TextDocument doc ) {
452
+ if (body == null ) {
453
+ return ;
454
+ }
455
+
456
+ body .accept (new ASTVisitor () {
457
+
458
+ @ Override
459
+ public boolean visit (MethodInvocation methodInvocation ) {
460
+ try {
461
+ String methodName = methodInvocation .getName ().toString ();
462
+ if ("registerBean" .equals (methodName )) {
463
+
464
+ IMethodBinding methodBinding = methodInvocation .resolveMethodBinding ();
465
+ ITypeBinding declaringClass = methodBinding .getDeclaringClass ();
466
+
467
+ if (declaringClass != null && Annotations .BEAN_REGISTRY_INTERFACE .equals (declaringClass .getQualifiedName ())) {
468
+
469
+ @ SuppressWarnings ("unchecked" )
470
+ List <Expression > arguments = methodInvocation .arguments ();
471
+ List <ITypeBinding > types = new ArrayList <>();
472
+
473
+ for (Expression argument : arguments ) {
474
+ ITypeBinding typeBinding = argument .resolveTypeBinding ();
475
+ if (typeBinding != null ) {
476
+ types .add (typeBinding );
477
+ }
478
+ else {
479
+ return true ;
480
+ }
481
+ }
482
+
483
+ if (arguments .size () == 1 && "java.lang.Class" .equals (types .get (0 ).getBinaryName ())) {
484
+ // <T> String registerBean(Class<T> beanClass);
485
+
486
+ ITypeBinding typeBinding = types .get (0 );
487
+ ITypeBinding [] typeParameters = typeBinding .getTypeArguments ();
488
+ if (typeParameters != null && typeParameters .length == 1 ) {
489
+ String typeParamName = typeParameters [0 ].getBinaryName ();
490
+
491
+ String beanName = BeanUtils .getBeanNameFromType (typeParameters [0 ].getName ());
492
+ String beanType = typeParamName ;
493
+
494
+ createBean (component , beanName , beanType , typeParameters [0 ], methodInvocation , context , doc );
495
+ }
496
+ }
497
+ else if (arguments .size () == 2 && "java.lang.String" .equals (types .get (0 ).getQualifiedName ()) && "java.lang.Class" .equals (types .get (1 ).getBinaryName ())) {
498
+ // <T> void registerBean(String name, Class<T> beanClass);
499
+
500
+ String beanName = ASTUtils .getExpressionValueAsString (arguments .get (0 ), (dep ) -> {});
501
+
502
+ ITypeBinding typeBinding = types .get (1 );
503
+ ITypeBinding [] typeParameters = typeBinding .getTypeArguments ();
504
+ if (typeParameters != null && typeParameters .length == 1 ) {
505
+ String typeParamName = typeParameters [0 ].getBinaryName ();
506
+ String beanType = typeParamName ;
507
+
508
+ createBean (component , beanName , beanType , typeParameters [0 ], methodInvocation , context , doc );
509
+ }
510
+ }
511
+ else if (arguments .size () == 2 && "java.lang.Class" .equals (types .get (0 ).getBinaryName ()) && "java.util.function.Consumer" .equals (types .get (1 ).getBinaryName ())) {
512
+ // <T> String registerBean(Class<T> beanClass, Consumer<Spec<T>> customizer);
513
+
514
+ ITypeBinding typeBinding = types .get (0 );
515
+ ITypeBinding [] typeParameters = typeBinding .getTypeArguments ();
516
+ if (typeParameters != null && typeParameters .length == 1 ) {
517
+ String typeParamName = typeParameters [0 ].getBinaryName ();
518
+
519
+ String beanName = BeanUtils .getBeanNameFromType (typeParameters [0 ].getName ());
520
+ String beanType = typeParamName ;
521
+
522
+ createBean (component , beanName , beanType , typeParameters [0 ], methodInvocation , context , doc );
523
+ }
524
+ }
525
+ else if (arguments .size () == 3 && "java.lang.String" .equals (types .get (0 ).getQualifiedName ())
526
+ && "java.lang.Class" .equals (types .get (1 ).getBinaryName ()) && "java.util.function.Consumer" .equals (types .get (2 ).getBinaryName ())) {
527
+ // <T> void registerBean(String name, Class<T> beanClass, Consumer<Spec<T>> customizer);
528
+
529
+ String beanName = ASTUtils .getExpressionValueAsString (arguments .get (0 ), (dep ) -> {});
530
+
531
+ ITypeBinding typeBinding = types .get (1 );
532
+ ITypeBinding [] typeParameters = typeBinding .getTypeArguments ();
533
+ if (typeParameters != null && typeParameters .length == 1 ) {
534
+ String typeParamName = typeParameters [0 ].getBinaryName ();
535
+ String beanType = typeParamName ;
536
+
537
+ createBean (component , beanName , beanType , typeParameters [0 ], methodInvocation , context , doc );
538
+ }
539
+ }
540
+ }
541
+ }
542
+
543
+ } catch (BadLocationException e ) {
544
+ log .error ("" , e );
545
+ }
546
+ return super .visit (methodInvocation );
547
+ }
548
+ });
549
+ }
550
+
551
+ public void createBean (Bean parentBean , String beanName , String beanType , ITypeBinding beanTypeBinding , ASTNode node , SpringIndexerJavaContext context , TextDocument doc ) throws BadLocationException {
552
+ Location location = new Location (doc .getUri (), doc .toRange (node .getStartPosition (), node .getLength ()));
553
+
554
+ WorkspaceSymbol symbol = new WorkspaceSymbol (
555
+ beanLabel ("+" , null , null , beanName , beanType ),
556
+ SymbolKind .Class ,
557
+ Either .forLeft (location ));
558
+ context .getGeneratedSymbols ().add (new CachedSymbol (context .getDocURI (), context .getLastModified (), symbol ));
559
+
560
+ InjectionPoint [] injectionPoints = DefaultValues .EMPTY_INJECTION_POINTS ;
561
+ Set <String > supertypes = new HashSet <>();
562
+ ASTUtils .findSupertypes (beanTypeBinding , supertypes );
563
+
564
+ AnnotationMetadata [] annotations = DefaultValues .EMPTY_ANNOTATIONS ;
565
+
566
+ Bean bean = new Bean (beanName , beanType , location , injectionPoints , supertypes , annotations , false , symbol .getName ());
567
+ parentBean .addChild (bean );
568
+ }
569
+
380
570
public static String beanLabel (String searchPrefix , String annotationTypeName , Collection <String > metaAnnotationNames , String beanName , String beanType ) {
381
571
StringBuilder symbolLabel = new StringBuilder ();
382
572
symbolLabel .append ("@" );
@@ -385,21 +575,25 @@ public static String beanLabel(String searchPrefix, String annotationTypeName, C
385
575
symbolLabel .append ('\'' );
386
576
symbolLabel .append (beanName );
387
577
symbolLabel .append ('\'' );
388
- symbolLabel .append (" (@" );
389
- symbolLabel .append (annotationTypeName );
390
- if (!metaAnnotationNames .isEmpty ()) {
391
- symbolLabel .append (" <: " );
392
- boolean first = true ;
393
- for (String ma : metaAnnotationNames ) {
394
- if (!first ) {
395
- symbolLabel .append (", " );
578
+
579
+ if (annotationTypeName != null ) {
580
+ symbolLabel .append (" (@" );
581
+ symbolLabel .append (annotationTypeName );
582
+ if (!metaAnnotationNames .isEmpty ()) {
583
+ symbolLabel .append (" <: " );
584
+ boolean first = true ;
585
+ for (String ma : metaAnnotationNames ) {
586
+ if (!first ) {
587
+ symbolLabel .append (", " );
588
+ }
589
+ symbolLabel .append ("@" );
590
+ symbolLabel .append (ma );
591
+ first = false ;
396
592
}
397
- symbolLabel .append ("@" );
398
- symbolLabel .append (ma );
399
- first = false ;
400
593
}
594
+ symbolLabel .append (")" );
401
595
}
402
- symbolLabel .append (") " );
596
+ symbolLabel .append (" " );
403
597
symbolLabel .append (beanType );
404
598
return symbolLabel .toString ();
405
599
}
0 commit comments