diff --git a/headless-services/commons/commons-rewrite/src/main/java/org/openrewrite/java/spring/framework/BeanMethodsNotPublic.java b/headless-services/commons/commons-rewrite/src/main/java/org/openrewrite/java/spring/framework/BeanMethodsNotPublic.java deleted file mode 100644 index eb3cc38889..0000000000 --- a/headless-services/commons/commons-rewrite/src/main/java/org/openrewrite/java/spring/framework/BeanMethodsNotPublic.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2021 the original author or authors. - *
- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *
- * https://www.apache.org/licenses/LICENSE-2.0 - *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.openrewrite.java.spring.framework;
-
-import org.openrewrite.ExecutionContext;
-import org.openrewrite.Preconditions;
-import org.openrewrite.Recipe;
-import org.openrewrite.TreeVisitor;
-import org.openrewrite.java.AnnotationMatcher;
-import org.openrewrite.java.ChangeMethodAccessLevelVisitor;
-import org.openrewrite.java.JavaIsoVisitor;
-import org.openrewrite.java.MethodMatcher;
-import org.openrewrite.java.search.UsesType;
-import org.openrewrite.java.service.AnnotationService;
-import org.openrewrite.java.tree.J;
-import org.openrewrite.java.tree.TypeUtils;
-
-public class BeanMethodsNotPublic extends Recipe {
- private static final String BEAN = "org.springframework.context.annotation.Bean";
- private static final AnnotationMatcher BEAN_ANNOTATION_MATCHER = new AnnotationMatcher("@" + BEAN);
-
- @Override
- public String getDisplayName() {
- return "Remove `public` from `@Bean` methods";
- }
-
- @Override
- public String getDescription() {
- return "Remove public modifier from `@Bean` methods. They no longer have to be public visibility to be usable by Spring.";
- }
-
- @Override
- public TreeVisitor, ExecutionContext> getVisitor() {
- return Preconditions.check(new UsesType<>(BEAN, false), new JavaIsoVisitor
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.openrewrite.java.spring.framework;
-
-import org.junit.jupiter.api.Test;
-import org.openrewrite.Issue;
-import org.openrewrite.java.JavaParser;
-import org.openrewrite.test.RecipeSpec;
-import org.openrewrite.test.RewriteTest;
-
-import static org.openrewrite.java.Assertions.java;
-
-class BeanMethodsNotPublicTest implements RewriteTest {
-
- @Override
- public void defaults(RecipeSpec spec) {
- spec.recipe(new BeanMethodsNotPublic())
- .parser(JavaParser.fromJavaVersion().classpath("spring-context"));
- }
-
- @Test
- void removePublicModifierFromBeanMethods() {
- //language=java
- rewriteRun(
- java(
- """
- package a.b.c;
- public class DataSource {}
- """),
- java(
- """
- import a.b.c.DataSource;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Primary;
-
- public class DatabaseConfiguration {
-
- // primary comments
- @Primary
- @Bean
- public DataSource dataSource() {
- return new DataSource();
- }
-
- @Bean // comments
- public final DataSource dataSource2() {
- return new DataSource();
- }
-
- @Bean
- // comments
- public static DataSource dataSource3() {
- return new DataSource();
- }
- }
- """,
- """
- import a.b.c.DataSource;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Primary;
-
- public class DatabaseConfiguration {
-
- // primary comments
- @Primary
- @Bean
- DataSource dataSource() {
- return new DataSource();
- }
-
- @Bean // comments
- final DataSource dataSource2() {
- return new DataSource();
- }
-
- @Bean
- // comments
- static DataSource dataSource3() {
- return new DataSource();
- }
- }
- """
- )
- );
- }
-
- @Issue("https://github.com/openrewrite/rewrite-spring/issues/70")
- @Test
- void leaveOverridesUnchanged() {
- //language=java
- rewriteRun(
- java(
- """
- interface A {
- void a();
- }
- """
- ),
- java(
- """
- class B {
- public void b() {}
- }
- """
- ),
- java(
- """
- import org.springframework.context.annotation.Bean;
-
- public class PublicBeans extends B implements A {
- @Bean
- public void a() {}
-
- @Bean
- public void b() {}
- }
- """
- )
- );
- }
-}
diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/jdt/refactoring/ChangeMethodVisibilityRefactoring.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/jdt/refactoring/ChangeMethodVisibilityRefactoring.java
new file mode 100644
index 0000000000..e6349cc99e
--- /dev/null
+++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/jdt/refactoring/ChangeMethodVisibilityRefactoring.java
@@ -0,0 +1,97 @@
+/*******************************************************************************
+ * Copyright (c) 2026 VMware, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VMware, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.springframework.ide.vscode.boot.java.jdt.refactoring;
+
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTVisitor;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.Modifier;
+import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
+
+public class ChangeMethodVisibilityRefactoring implements JdtRefactoring {
+
+ public enum Visibility {
+ PUBLIC, PROTECTED, PACKAGE_PRIVATE, PRIVATE
+ }
+
+ private final Visibility targetVisibility;
+ private final int[] methodOffsets;
+
+ public ChangeMethodVisibilityRefactoring(Visibility targetVisibility, int... methodOffsets) {
+ this.targetVisibility = targetVisibility;
+ this.methodOffsets = methodOffsets;
+ }
+
+ @Override
+ public void apply(ASTRewrite rewrite, CompilationUnit cu) {
+ AST ast = cu.getAST();
+ for (int offset : methodOffsets) {
+ MethodDeclaration method = findMethodAtOffset(cu, offset);
+ if (method != null) {
+ ListRewrite modifiersRewrite = rewrite.getListRewrite(method, MethodDeclaration.MODIFIERS2_PROPERTY);
+
+ // Remove existing visibility modifiers
+ Modifier existingVisibilityModifier = null;
+ for (Object mod : method.modifiers()) {
+ if (mod instanceof Modifier modifier) {
+ if (modifier.isPublic() || modifier.isProtected() || modifier.isPrivate()) {
+ existingVisibilityModifier = modifier;
+ modifiersRewrite.remove(modifier, null);
+ break;
+ }
+ }
+ }
+
+ // Add new visibility modifier if not package private
+ ModifierKeyword keyword = getModifierKeyword(targetVisibility);
+ if (keyword != null) {
+ Modifier newModifier = ast.newModifier(keyword);
+ if (existingVisibilityModifier != null) {
+ modifiersRewrite.insertAfter(newModifier, existingVisibilityModifier, null);
+ } else {
+ modifiersRewrite.insertFirst(newModifier, null);
+ }
+ }
+ }
+ }
+ }
+
+ private ModifierKeyword getModifierKeyword(Visibility visibility) {
+ switch (visibility) {
+ case PUBLIC:
+ return ModifierKeyword.PUBLIC_KEYWORD;
+ case PROTECTED:
+ return ModifierKeyword.PROTECTED_KEYWORD;
+ case PRIVATE:
+ return ModifierKeyword.PRIVATE_KEYWORD;
+ case PACKAGE_PRIVATE:
+ default:
+ return null;
+ }
+ }
+
+ private MethodDeclaration findMethodAtOffset(CompilationUnit cu, int offset) {
+ MethodDeclaration[] result = new MethodDeclaration[1];
+ cu.accept(new ASTVisitor() {
+ @Override
+ public boolean visit(MethodDeclaration node) {
+ if (node.getStartPosition() == offset) {
+ result[0] = node;
+ }
+ return result[0] == null; // stop visiting if found
+ }
+ });
+ return result[0];
+ }
+}
diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/BeanMethodNotPublicReconciler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/BeanMethodNotPublicReconciler.java
index 0aa28fda52..7c0495ea81 100644
--- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/BeanMethodNotPublicReconciler.java
+++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/reconcilers/BeanMethodNotPublicReconciler.java
@@ -24,20 +24,23 @@
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
-import org.openrewrite.java.spring.framework.BeanMethodsNotPublic;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.java.Annotations;
import org.springframework.ide.vscode.boot.java.Boot2JavaProblemType;
+import org.springframework.ide.vscode.boot.java.jdt.refactoring.ChangeMethodVisibilityRefactoring;
+import org.springframework.ide.vscode.boot.java.jdt.refactoring.ChangeMethodVisibilityRefactoring.Visibility;
+import org.springframework.ide.vscode.boot.java.jdt.refactoring.JdtFixDescriptor;
+import org.springframework.ide.vscode.boot.java.jdt.refactoring.JdtRefactorings;
import org.springframework.ide.vscode.commons.Version;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.java.SpringProjectUtil;
+import org.springframework.ide.vscode.commons.languageserver.quickfix.Quickfix.QuickfixData;
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixRegistry;
+import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixType;
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl;
-import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope;
-import org.springframework.ide.vscode.commons.rewrite.java.FixDescriptor;
public class BeanMethodNotPublicReconciler implements JdtAstReconciler {
@@ -65,11 +68,14 @@ public ProblemType getProblemType() {
public ASTVisitor createVisitor(IJavaProject project, URI docUri, CompilationUnit cu, ReconcilingContext context) {
return new ASTVisitor() {
+
+ private final List