11/*******************************************************************************
2- * Copyright (c) 2023, 2204 VMware, Inc.
2+ * Copyright (c) 2023, 2024 VMware, Inc.
33 * All rights reserved. This program and the accompanying materials
44 * are made available under the terms of the Eclipse Public License v1.0
55 * which accompanies this distribution, and is available at
2727import org .eclipse .jdt .core .dom .ITypeBinding ;
2828import org .eclipse .jdt .core .dom .Modifier ;
2929import org .eclipse .jdt .core .dom .TypeDeclaration ;
30- import org .eclipse .lsp4j .WorkspaceSymbol ;
31- import org .springframework .beans .BeansException ;
32- import org .springframework .context .ApplicationContext ;
33- import org .springframework .context .ApplicationContextAware ;
34- import org .springframework .ide .vscode .boot .app .SpringSymbolIndex ;
30+ import org .springframework .ide .vscode .boot .index .SpringMetamodelIndex ;
3531import org .springframework .ide .vscode .boot .java .Annotations ;
3632import org .springframework .ide .vscode .boot .java .SpringAotJavaProblemType ;
37- import org .springframework .ide .vscode .boot .java .beans .BeansSymbolAddOnInformation ;
38- import org .springframework .ide .vscode .boot .java .beans .ConfigBeanSymbolAddOnInformation ;
39- import org .springframework .ide .vscode .boot .java .handlers .EnhancedSymbolInformation ;
40- import org .springframework .ide .vscode .boot .java .handlers .SymbolAddOnInformation ;
4133import org .springframework .ide .vscode .commons .java .IClasspathUtil ;
4234import org .springframework .ide .vscode .commons .java .IJavaProject ;
4335import org .springframework .ide .vscode .commons .languageserver .quickfix .QuickfixRegistry ;
4436import org .springframework .ide .vscode .commons .languageserver .reconcile .IProblemCollector ;
4537import org .springframework .ide .vscode .commons .languageserver .reconcile .ProblemType ;
4638import org .springframework .ide .vscode .commons .languageserver .reconcile .ReconcileProblemImpl ;
39+ import org .springframework .ide .vscode .commons .protocol .spring .Bean ;
4740import org .springframework .ide .vscode .commons .rewrite .config .RecipeScope ;
4841import org .springframework .ide .vscode .commons .rewrite .java .DefineMethod ;
4942import org .springframework .ide .vscode .commons .rewrite .java .FixDescriptor ;
5245import com .google .common .collect .ImmutableList .Builder ;
5346import com .google .common .collect .ImmutableSet ;
5447
55- public class NotRegisteredBeansReconciler implements JdtAstReconciler , ApplicationContextAware {
48+ public class NotRegisteredBeansReconciler implements JdtAstReconciler {
5649
5750 private static final List <String > AOT_BEANS = List .of (
5851 "org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor" ,
5952 "org.springframework.beans.factory.aot.BeanRegistrationAotProcessor"
6053 );
6154
62- private ApplicationContext applicationContext ;
63-
6455 private QuickfixRegistry registry ;
56+ private SpringMetamodelIndex springIndex ;
6557
66- public NotRegisteredBeansReconciler (QuickfixRegistry registry ) {
67- this .registry = registry ;
58+ public NotRegisteredBeansReconciler (QuickfixRegistry registry , SpringMetamodelIndex springIndex ) {
59+ this .registry = registry ;
60+ this .springIndex = springIndex ;
6861 }
6962
7063 @ Override
@@ -77,11 +70,6 @@ public ProblemType getProblemType() {
7770 return SpringAotJavaProblemType .JAVA_BEAN_NOT_REGISTERED_IN_AOT ;
7871 }
7972
80- @ Override
81- public void setApplicationContext (ApplicationContext applicationContext ) throws BeansException {
82- this .applicationContext = applicationContext ;
83- }
84-
8573 @ Override
8674 public ASTVisitor createVisitor (IJavaProject project , URI docUri , CompilationUnit cu , IProblemCollector problemCollector , boolean isCompleteAst ) {
8775
@@ -90,79 +78,81 @@ public ASTVisitor createVisitor(IJavaProject project, URI docUri, CompilationUni
9078 @ Override
9179 public boolean visit (TypeDeclaration node ) {
9280 if (!node .isInterface () && !Modifier .isAbstract (node .getModifiers ())) {
81+
9382 ITypeBinding type = node .resolveBinding ();
9483 if (type != null && ReconcileUtils .implementsAnyType (AOT_BEANS , type )) {
95- String beanClassName =type .getQualifiedName ();
96- SpringSymbolIndex index = applicationContext .getBean (SpringSymbolIndex .class );
97- List <WorkspaceSymbol > beanSymbols = index .getSymbols (data -> {
98- SymbolAddOnInformation [] additionalInformation = data .getAdditionalInformation ();
99- if (additionalInformation != null ) {
100- for (SymbolAddOnInformation info : additionalInformation ) {
101- if (info instanceof BeansSymbolAddOnInformation ) {
102- BeansSymbolAddOnInformation info2 = (BeansSymbolAddOnInformation ) info ;
103- return beanClassName .equals (info2 .getBeanType ());
104- }
105- }
106- }
107- return false ;
108- }).limit (1 ).collect (Collectors .toList ());
84+ String beanClassName = type .getQualifiedName ();
10985
110- if (beanSymbols .isEmpty ()) {
111- Builder <FixDescriptor > fixListBuilder = ImmutableList .builder ();
112- for (EnhancedSymbolInformation s : index .getEnhancedSymbols (project )) {
113- if (s .getAdditionalInformation () != null ) {
114- ConfigBeanSymbolAddOnInformation configInfo = Arrays .stream (s .getAdditionalInformation ()).filter (ConfigBeanSymbolAddOnInformation .class ::isInstance ).map (ConfigBeanSymbolAddOnInformation .class ::cast ).findFirst ().orElse (null );
115- if (configInfo != null ) {
116- for (IMethodBinding constructor : type .getDeclaredMethods ()) {
117- if (constructor .isConstructor ()) {
118- String constructorParamsSignature = "(" + Arrays .stream (constructor .getParameterTypes ()).map (pt -> typePattern (pt )).collect (Collectors .joining ("," )) + ")" ;
119- String beanMethodName = "get" + type .getName ();
120- String pattern = beanMethodName + constructorParamsSignature ;
121- String contructorParamsLabel = "(" + Arrays .stream (constructor .getParameterTypes ()).map (NotRegisteredBeansReconciler ::typeStr ).collect (Collectors .joining (", " )) + ")" ;
122-
123- Builder <String > paramBuilder = ImmutableList .builder ();
124- for (int i = 0 ; i < constructor .getParameterNames ().length && i < constructor .getParameterTypes ().length ; i ++) {
125- ITypeBinding paramType = constructor .getParameterTypes ()[i ];
126- String paramName = constructor .getParameterNames ()[i ];
127- paramBuilder .add (typeStr (paramType ) + ' ' + paramName );
128- }
129- String paramsStr = String .join (", " , paramBuilder .build ().toArray (String []::new ));
130-
131- final Set <String > allFqTypes = new HashSet <>();
132- allFqTypes .add (Annotations .BEAN );
133- allFqTypes .addAll (allFQTypes (constructor ));
134- fixListBuilder .add (new FixDescriptor (DefineMethod .class .getName (), List .of (s .getSymbol ().getLocation ().getLeft ().getUri ()), "Define bean in config '" + configInfo .getBeanID () + "' with constructor " + contructorParamsLabel )
135- .withRecipeScope (RecipeScope .FILE )
136- .withParameters (Map .of (
137- "targetFqName" , configInfo .getBeanType (),
138- "signature" , pattern ,
139- "template" , "@Bean\n "
140- + type .getName () + " " + beanMethodName + "(" + paramsStr + ") {\n "
141- + "return new " + type .getName () + "(" + Arrays .stream (constructor .getParameterNames ()).collect (Collectors .joining (", " )) + ");\n "
142- + "}\n " ,
143- "imports" , allFqTypes .toArray (String []::new ),
144- "typeStubs" , new String [0 ]/*new String[] { source.printAll() }*/ ,
145- "classpath" , IClasspathUtil .getAllBinaryRoots (project .getClasspath ()).stream ().map (f -> f .toPath ().toString ()).toArray (String []::new )
146-
147- ))
148- );
149- }
150- }
151- }
152- }
153- }
154- ReconcileProblemImpl problem = new ReconcileProblemImpl (getProblemType (), getProblemType ().getLabel (), node .getName ().getStartPosition (), node .getName ().getLength ());
155- ReconcileUtils .setRewriteFixes (registry , problem , fixListBuilder .build ());
156- problemCollector .accept (problem );
86+ Bean [] registeredBeans = springIndex .getBeansWithType (project .getElementName (), beanClassName );
87+
88+ if (registeredBeans == null || registeredBeans .length == 0 ) {
89+ createProblemAndQuickFixes (project , problemCollector , node , type );
15790 }
15891 }
15992 }
16093 return super .visit (node );
16194 }
162-
16395 };
16496 }
16597
98+ protected void createProblemAndQuickFixes (IJavaProject project , IProblemCollector problemCollector , TypeDeclaration node , ITypeBinding type ) {
99+ Builder <FixDescriptor > fixListBuilder = ImmutableList .builder ();
100+
101+ Bean [] configBeans = getConfigurationBeans (project );
102+
103+ for (Bean configBean : configBeans ) {
104+ for (IMethodBinding constructor : type .getDeclaredMethods ()) {
105+ if (constructor .isConstructor ()) {
106+ String constructorParamsSignature = "(" + Arrays .stream (constructor .getParameterTypes ()).map (pt -> typePattern (pt )).collect (Collectors .joining ("," )) + ")" ;
107+ String beanMethodName = "get" + type .getName ();
108+ String pattern = beanMethodName + constructorParamsSignature ;
109+ String contructorParamsLabel = "(" + Arrays .stream (constructor .getParameterTypes ()).map (NotRegisteredBeansReconciler ::typeStr ).collect (Collectors .joining (", " )) + ")" ;
110+
111+ Builder <String > paramBuilder = ImmutableList .builder ();
112+ for (int i = 0 ; i < constructor .getParameterNames ().length && i < constructor .getParameterTypes ().length ; i ++) {
113+ ITypeBinding paramType = constructor .getParameterTypes ()[i ];
114+ String paramName = constructor .getParameterNames ()[i ];
115+ paramBuilder .add (typeStr (paramType ) + ' ' + paramName );
116+ }
117+ String paramsStr = String .join (", " , paramBuilder .build ().toArray (String []::new ));
118+
119+ final Set <String > allFqTypes = new HashSet <>();
120+ allFqTypes .add (Annotations .BEAN );
121+ allFqTypes .addAll (allFQTypes (constructor ));
122+ fixListBuilder .add (new FixDescriptor (DefineMethod .class .getName (), List .of (configBean .getLocation ().getUri ()), "Define bean in config '" + configBean .getName () + "' with constructor " + contructorParamsLabel )
123+ .withRecipeScope (RecipeScope .FILE )
124+ .withParameters (Map .of (
125+ "targetFqName" , configBean .getType (),
126+ "signature" , pattern ,
127+ "template" , "@Bean\n "
128+ + type .getName () + " " + beanMethodName + "(" + paramsStr + ") {\n "
129+ + "return new " + type .getName () + "(" + Arrays .stream (constructor .getParameterNames ()).collect (Collectors .joining (", " )) + ");\n "
130+ + "}\n " ,
131+ "imports" , allFqTypes .toArray (String []::new ),
132+ "typeStubs" , new String [0 ]/*new String[] { source.printAll() }*/ ,
133+ "classpath" , IClasspathUtil .getAllBinaryRoots (project .getClasspath ()).stream ().map (f -> f .toPath ().toString ()).toArray (String []::new )
134+
135+ ))
136+ );
137+ }
138+ }
139+ }
140+
141+ ReconcileProblemImpl problem = new ReconcileProblemImpl (getProblemType (), getProblemType ().getLabel (), node .getName ().getStartPosition (), node .getName ().getLength ());
142+ ReconcileUtils .setRewriteFixes (registry , problem , fixListBuilder .build ());
143+ problemCollector .accept (problem );
144+ }
145+
146+ private Bean [] getConfigurationBeans (IJavaProject project ) {
147+ Bean [] beans = springIndex .getBeansOfProject (project .getElementName ());
148+ if (beans != null ) {
149+ return Arrays .stream (beans ).filter (bean -> bean .isConfiguration ()).toArray (Bean []::new );
150+ }
151+ else {
152+ return new Bean [0 ];
153+ }
154+ }
155+
166156 private static Set <String > allFQTypes (IBinding binding ) {
167157 ImmutableSet .Builder <String > b = ImmutableSet .builder ();
168158 if (binding instanceof IMethodBinding ) {
0 commit comments