From fb3f20c3ea400926030d6361c179a274faac2023 Mon Sep 17 00:00:00 2001 From: aboyko Date: Tue, 29 Nov 2022 18:34:10 -0500 Subject: [PATCH] Unnecessary @Repository problem and quickfix --- .../boot/java/Boot2JavaProblemType.java | 4 +- .../rewrite/BootCodeActionRepository.java | 4 +- .../boot/java/rewrite/RewriteReconciler.java | 2 +- .../reconcile/NoRepoAnnotationProblem.java | 100 ++++++++++++++++++ .../src/main/resources/problem-types.json | 6 ++ .../vscode-spring-boot/package.json | 12 +++ 6 files changed, 125 insertions(+), 3 deletions(-) create mode 100644 headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/reconcile/NoRepoAnnotationProblem.java diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/Boot2JavaProblemType.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/Boot2JavaProblemType.java index 083fe6a937..ba8610119d 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/Boot2JavaProblemType.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/Boot2JavaProblemType.java @@ -29,7 +29,9 @@ public enum Boot2JavaProblemType implements ProblemType { JAVA_CONSTRUCTOR_PARAMETER_INJECTION(IGNORE, "Use constructor parameter injection", "Use constructor parameter injection"), - JAVA_PRECISE_REQUEST_MAPPING(HINT, "Use precise mapping annotation, i.e. '@GetMapping', '@PostMapping', etc.", "Use precise mapping annotation, i.e. '@GetMapping', '@PostMapping', etc."); + JAVA_PRECISE_REQUEST_MAPPING(HINT, "Use precise mapping annotation, i.e. '@GetMapping', '@PostMapping', etc.", "Use precise mapping annotation, i.e. '@GetMapping', '@PostMapping', etc."), + + JAVA_REPOSITORY(WARNING, "Unnecessary `@Repository`", "Unnecessary `@Repository`"); private final ProblemSeverity defaultSeverity; private String description; diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/BootCodeActionRepository.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/BootCodeActionRepository.java index de26ddaf91..51b9f9d871 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/BootCodeActionRepository.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/BootCodeActionRepository.java @@ -17,6 +17,7 @@ import org.springframework.ide.vscode.boot.java.rewrite.reconcile.BeanPostProcessingIgnoreInAotProblem; import org.springframework.ide.vscode.boot.java.rewrite.reconcile.Boot3NotSupportedTypeProblem; import org.springframework.ide.vscode.boot.java.rewrite.reconcile.NoAutowiredOnConstructorProblem; +import org.springframework.ide.vscode.boot.java.rewrite.reconcile.NoRepoAnnotationProblem; import org.springframework.ide.vscode.boot.java.rewrite.reconcile.NoRequestMappingAnnotationCodeAction; import org.springframework.ide.vscode.boot.java.rewrite.reconcile.NotRegisteredBeansProblem; import org.springframework.ide.vscode.boot.java.rewrite.reconcile.PreciseBeanTypeProblem; @@ -37,7 +38,8 @@ public List getCodeActionDescriptors() { new NotRegisteredBeansProblem(), new Boot3NotSupportedTypeProblem(), new NoRequestMappingAnnotationCodeAction(), - new AutowiredFieldIntoConstructorParameterCodeAction() + new AutowiredFieldIntoConstructorParameterCodeAction(), + new NoRepoAnnotationProblem() ); } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteReconciler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteReconciler.java index 83dbac479c..abd75d6bf9 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteReconciler.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/RewriteReconciler.java @@ -123,7 +123,7 @@ private ReconcileProblemImpl createProblem(IDocument doc, RecipeCodeActionDescri QuickfixType quickfixType = quickfixRegistry.getQuickfixType(RewriteRefactorings.REWRITE_RECIPE_QUICKFIX); if (quickfixType != null) { for (FixDescriptor f : m.getFixes()) { - if (recipeRepo.getRecipe(f.getRecipeId()) != null) { + if (recipeRepo.getRecipe(f.getRecipeId()).isPresent()) { problem.addQuickfix(new QuickfixData<>( quickfixType, f, diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/reconcile/NoRepoAnnotationProblem.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/reconcile/NoRepoAnnotationProblem.java new file mode 100644 index 0000000000..36c0a09d2a --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/rewrite/reconcile/NoRepoAnnotationProblem.java @@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright (c) 2022 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.rewrite.reconcile; + +import static org.springframework.ide.vscode.commons.java.SpringProjectUtil.springBootVersionGreaterOrEqual; + +import java.util.List; + +import org.openrewrite.ExecutionContext; +import org.openrewrite.SourceFile; +import org.openrewrite.Tree; +import org.openrewrite.internal.ListUtils; +import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.JavaVisitor; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.J.ClassDeclaration; +import org.openrewrite.marker.Range; +import org.openrewrite.java.tree.JavaType; +import org.openrewrite.java.tree.TypeUtils; +import org.springframework.context.ApplicationContext; +import org.springframework.ide.vscode.boot.java.Annotations; +import org.springframework.ide.vscode.boot.java.Boot2JavaProblemType; +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.rewrite.config.RecipeCodeActionDescriptor; +import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope; +import org.springframework.ide.vscode.commons.rewrite.java.FixAssistMarker; +import org.springframework.ide.vscode.commons.rewrite.java.FixDescriptor; + +public class NoRepoAnnotationProblem implements RecipeCodeActionDescriptor { + + private static final String ID = "org.openrewrite.java.spring.NoRepoAnnotationOnRepoInterface"; + private static final String LABEL = "Remove Unnecessary @Repository"; + private static final String INTERFACE_REPOSITORY = "org.springframework.data.repository.Repository"; + private static final String ANNOTATION_REPOSITORY = Annotations.REPOSITORY; + + @Override + public JavaVisitor getMarkerVisitor(ApplicationContext applicationContext) { + return new JavaIsoVisitor() { + + @Override + public ClassDeclaration visitClassDeclaration(ClassDeclaration classDecl, + ExecutionContext executionContext) { + J.ClassDeclaration c = super.visitClassDeclaration(classDecl, executionContext); + if (c.getKind() == ClassDeclaration.Kind.Type.Interface) { + final J.Annotation repoAnnotation = c.getLeadingAnnotations().stream().filter(annotation -> { + if (annotation.getArguments() == null || annotation.getArguments().isEmpty() + || annotation.getArguments().get(0) instanceof J.Empty) { + JavaType.FullyQualified type = TypeUtils.asFullyQualified(annotation.getType()); + return type != null && ANNOTATION_REPOSITORY.equals(type.getFullyQualifiedName()); + } + return false; + }).findFirst().orElse(null); + if (repoAnnotation != null && TypeUtils.isAssignableTo(INTERFACE_REPOSITORY, c.getType())) { + c = c.withLeadingAnnotations(ListUtils.map(c.getLeadingAnnotations(), a -> { + if (a == repoAnnotation) { + String uri = getCursor().firstEnclosing(SourceFile.class).getSourcePath().toUri() + .toString(); + FixAssistMarker fixAssistMarker = new FixAssistMarker(Tree.randomId(), getId()).withFixes( + new FixDescriptor(ID, List.of(uri), LABEL) + .withRangeScope(classDecl.getMarkers().findFirst(Range.class).get()) + .withRecipeScope(RecipeScope.NODE), + new FixDescriptor(ID, List.of(uri), + RecipeCodeActionDescriptor.buildLabel(LABEL, RecipeScope.FILE)) + .withRecipeScope(RecipeScope.FILE), + new FixDescriptor(ID, List.of(uri), + RecipeCodeActionDescriptor.buildLabel(LABEL, RecipeScope.PROJECT)) + .withRecipeScope(RecipeScope.PROJECT) + + ); + return a.withMarkers(a.getMarkers().add(fixAssistMarker)); + } + return a; + })); + } + } + return c; + } + + }; + } + + @Override + public boolean isApplicable(IJavaProject project) { + return springBootVersionGreaterOrEqual(2, 0, 0).test(project); + } + + @Override + public Boot2JavaProblemType getProblemType() { + return Boot2JavaProblemType.JAVA_REPOSITORY; + } + +} diff --git a/headless-services/spring-boot-language-server/src/main/resources/problem-types.json b/headless-services/spring-boot-language-server/src/main/resources/problem-types.json index 3721a58948..b95cc7e76d 100644 --- a/headless-services/spring-boot-language-server/src/main/resources/problem-types.json +++ b/headless-services/spring-boot-language-server/src/main/resources/problem-types.json @@ -43,6 +43,12 @@ "label": "Use precise mapping annotation, i.e. '@GetMapping', '@PostMapping', etc.", "description": "Use precise mapping annotation, i.e. '@GetMapping', '@PostMapping', etc.", "defaultSeverity": "HINT" + }, + { + "code": "JAVA_REPOSITORY", + "label": "Unnecessary `@Repository`", + "description": "Unnecessary `@Repository`", + "defaultSeverity": "WARNING" } ] }, diff --git a/vscode-extensions/vscode-spring-boot/package.json b/vscode-extensions/vscode-spring-boot/package.json index 40d0f04dd6..2f28fcf5f2 100644 --- a/vscode-extensions/vscode-spring-boot/package.json +++ b/vscode-extensions/vscode-spring-boot/package.json @@ -391,6 +391,18 @@ "HINT", "ERROR" ] + }, + "spring-boot.ls.problem.boot2.JAVA_REPOSITORY": { + "type": "string", + "default": "WARNING", + "description": "Unnecessary `@Repository`", + "enum": [ + "IGNORE", + "INFO", + "WARNING", + "HINT", + "ERROR" + ] } } },