Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace @Required on setters with @Autowired #612

Merged
merged 12 commits into from
Oct 29, 2024
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright 2024 the original author or authors.
* <p>
* 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
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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;

import org.openrewrite.ExecutionContext;
import org.openrewrite.Preconditions;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.java.*;
import org.openrewrite.java.search.FindAnnotations;
import org.openrewrite.java.search.UsesType;
import org.openrewrite.java.tree.J;

import java.util.Comparator;

public class ReplaceRequiredAnnotationOnSetterWithAutowired extends Recipe {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of a recipe would this also be possible with a declarative recipe ChangeType? Or are there edge cases that won't cover?

Whenever possible try to do declaratively what we can; that helps reduce what we need to maintain going forward.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good suggestion, Thanks!


public static final String ANNOTATION_REQUIRED_FQN = "org.springframework.beans.factory.annotation.Required";

@Override
public String getDisplayName() {
return "Replace `@Required` annotation on setter with `@Autowired`";
}

@Override
public String getDescription() {
return "Replace setter methods annotated with `@Required` with `@Autowired`.";
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return Preconditions.check(new UsesType<>(ANNOTATION_REQUIRED_FQN, false),
new ReplaceRequiredAnnotationVisitor());
}

private static class ReplaceRequiredAnnotationVisitor extends JavaIsoVisitor<ExecutionContext> {
@Override
public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
if (FindAnnotations.find(method, ANNOTATION_REQUIRED_FQN).isEmpty()) {
return method;
}

J.MethodDeclaration md = (J.MethodDeclaration) new RemoveAnnotationVisitor(new AnnotationMatcher("@" + ANNOTATION_REQUIRED_FQN))
.visit(method, ctx, getCursor().getParentOrThrow());
if (md != null) {
md = JavaTemplate.builder("@org.springframework.beans.factory.annotation.Autowired")
.javaParser(JavaParser.fromJavaVersion().dependsOn("package org.springframework.beans.factory.annotation;public interface Autowired {}"))
.build().apply(updateCursor(md), md.getCoordinates().addAnnotation(Comparator.comparing(J.Annotation::getSimpleName)));

maybeRemoveImport(ANNOTATION_REQUIRED_FQN);
doAfterVisit(ShortenFullyQualifiedTypeReferences.modifyOnly(md));
return md;
}
return method;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
/*
* Copyright 2024 the original author or authors.
* <p>
* 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
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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;

import org.junit.jupiter.api.Test;
import org.openrewrite.DocumentExample;
import org.openrewrite.java.JavaParser;
import org.openrewrite.test.RecipeSpec;
import org.openrewrite.test.RewriteTest;

import static org.openrewrite.java.Assertions.java;

class ReplaceRequiredAnnotationOnSetterWithAutowiredTest implements RewriteTest {
@Override
public void defaults(RecipeSpec spec) {
spec.recipe(new ReplaceRequiredAnnotationOnSetterWithAutowired())
.parser(JavaParser.fromJavaVersion().classpath("spring-beans"));
}

@DocumentExample
@Test
void replaceRequiredWithAutowired() {
//language=java
rewriteRun(
java(
"""
import org.springframework.beans.factory.annotation.Required;

class Foo {
private String a;

@Required
void setA(String a) {
this.a = a;
}
}
""",
"""
import org.springframework.beans.factory.annotation.Autowired;

class Foo {
private String a;

@Autowired
void setA(String a) {
this.a = a;
}
}
"""
)
);
}

@Test
void replaceRequiredWithAutowiredMultipleSetters() {
rewriteRun(
//language=java
java(
"""
import org.springframework.beans.factory.annotation.Required;

class Foo {
private String a;
private String b;

@Required
void setA(String a) {
this.a = a;
}

@Required
void setB(String b) {
this.b = b;
}
}
""",
"""
import org.springframework.beans.factory.annotation.Autowired;

class Foo {
private String a;
private String b;

@Autowired
void setA(String a) {
this.a = a;
}

@Autowired
void setB(String b) {
this.b = b;
}
}
"""
)
);
}

@Test
void replaceRequiredWithAutowiredMultipleSettersNotAllRequired() {
rewriteRun(
//language=java
java(
"""
import org.springframework.beans.factory.annotation.Required;

class Foo {
private String a;
private String b;

void setA(String a) {
this.a = a;
}

@Required
void setB(String b) {
this.b = b;
}
}
""",
"""
import org.springframework.beans.factory.annotation.Autowired;

class Foo {
private String a;
private String b;

void setA(String a) {
this.a = a;
}

@Autowired
void setB(String b) {
this.b = b;
}
}
"""
)
);
}

@Test
void setterWithoutAnnotationsShouldNotCauseChanges() {
rewriteRun(
//language=java
java(
"""
class Foo {
private String a;

void setA(String a) {
this.a = a;
}
}
"""
)
);
}
}