Skip to content

Commit b2e0b82

Browse files
committed
migrate recipe as-is
1 parent 3425b85 commit b2e0b82

File tree

2 files changed

+267
-0
lines changed

2 files changed

+267
-0
lines changed
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
* Copyright 2024 the original author or authors.
3+
* <p>
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.openrewrite.java.migrate.lombok;
17+
18+
import lombok.EqualsAndHashCode;
19+
import lombok.Value;
20+
import org.openrewrite.ExecutionContext;
21+
import org.openrewrite.Recipe;
22+
import org.openrewrite.TreeVisitor;
23+
import org.openrewrite.java.JavaIsoVisitor;
24+
import org.openrewrite.java.JavaParser;
25+
import org.openrewrite.java.JavaTemplate;
26+
import org.openrewrite.java.MethodMatcher;
27+
import org.openrewrite.java.tree.J;
28+
29+
import static java.util.Comparator.comparing;
30+
31+
@Value
32+
@EqualsAndHashCode(callSuper = false)
33+
public class NegligentlyConvertEquals extends Recipe {
34+
35+
@Override
36+
public String getDisplayName() {
37+
//language=markdown
38+
return "Replace any custom `equals` or `hashCode` methods with the `EqualsAndHashCode` annotation";
39+
}
40+
41+
@Override
42+
public String getDescription() {
43+
//language=markdown
44+
return "This recipe substitutes a class level `@EqualsAndHashCode` annotation for a custom `equals` or `hashCode` methods. " +
45+
"If both are defined, then both will be replaced. If only one is defined then it will be replaced. " +
46+
"This recipe does not check if the custom `equals` or `hashCode` methods behave like ones generated by the lombok annotation. " +
47+
"Doing so is considered infeasible at this time. " +
48+
"As a compromise this recipe finds and replaces the custom methods and relies on the user to review the changes closely. " +
49+
"As a consequence this recipe is VERY DANGEROUS to include into a composite recipe! " +
50+
"Users are advised to run it only in isolation.";
51+
}
52+
53+
@Override
54+
public TreeVisitor<?, ExecutionContext> getVisitor() {
55+
return new Converter();
56+
}
57+
58+
@Value
59+
@EqualsAndHashCode(callSuper = false)
60+
private static class Converter extends JavaIsoVisitor<ExecutionContext> {
61+
62+
MethodMatcher equalsMatcher = new MethodMatcher("* equals(Object)");
63+
MethodMatcher hashCodeMatcher = new MethodMatcher("* hashCode()");
64+
65+
@Override
66+
public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) {
67+
68+
J.ClassDeclaration classDeclAfterVisit = super.visitClassDeclaration(classDecl, ctx);
69+
70+
//only thing that can have changed is removal of either equals or hash code
71+
//and something needs to have changed before we add an annotation at class level
72+
if (classDeclAfterVisit != classDecl) {
73+
maybeAddImport("lombok.EqualsAndHashCode");
74+
75+
//Add annotation
76+
JavaTemplate template = JavaTemplate.builder("@EqualsAndHashCode\n")
77+
.imports("lombok.EqualsAndHashCode")
78+
.javaParser(JavaParser.fromJavaVersion().classpath("lombok"))
79+
.build();
80+
81+
return template.apply(
82+
updateCursor(classDeclAfterVisit),
83+
classDeclAfterVisit.getCoordinates().addAnnotation(comparing(J.Annotation::getSimpleName)));
84+
}
85+
return classDecl;
86+
}
87+
88+
89+
@Override
90+
public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
91+
J.ClassDeclaration classDecl = getCursor().firstEnclosingOrThrow(J.ClassDeclaration.class);
92+
93+
// The enclosing class of a J.MethodDeclaration must be known for a MethodMatcher to match it
94+
if (equalsMatcher.matches(method, classDecl) || hashCodeMatcher.matches(method, classDecl)) {
95+
return null;
96+
} else {
97+
return method;
98+
}
99+
}
100+
}
101+
}
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
/*
2+
* Copyright 2024 the original author or authors.
3+
* <p>
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.openrewrite.java.migrate.lombok;
17+
18+
import org.junit.jupiter.api.Test;
19+
import org.openrewrite.DocumentExample;
20+
import org.openrewrite.java.JavaParser;
21+
import org.openrewrite.test.RecipeSpec;
22+
import org.openrewrite.test.RewriteTest;
23+
24+
import static org.openrewrite.java.Assertions.java;
25+
26+
class NegligentlyConvertEqualsTest implements RewriteTest {
27+
28+
@Override
29+
public void defaults(RecipeSpec spec) {
30+
spec.recipe(new NegligentlyConvertEquals())
31+
.parser(JavaParser.fromJavaVersion().logCompilationWarningsAndErrors(true).classpath("lombok"));
32+
}
33+
34+
@DocumentExample
35+
@Test
36+
void replaceEquals() {
37+
rewriteRun(// language=java
38+
java(
39+
"""
40+
class A {
41+
42+
int foo;
43+
44+
@Override
45+
public boolean equals(Object o) {
46+
return false;
47+
}
48+
}
49+
""",
50+
"""
51+
import lombok.EqualsAndHashCode;
52+
53+
@EqualsAndHashCode
54+
class A {
55+
56+
int foo;
57+
}
58+
"""
59+
)
60+
);
61+
}
62+
63+
@Test
64+
void replaceEqualsInPackage() {
65+
rewriteRun(// language=java
66+
java(
67+
"""
68+
package com.example;
69+
70+
class A {
71+
72+
int foo;
73+
74+
@Override
75+
public boolean equals(Object o) {
76+
return false;
77+
}
78+
}
79+
""",
80+
"""
81+
package com.example;
82+
83+
import lombok.EqualsAndHashCode;
84+
85+
@EqualsAndHashCode
86+
class A {
87+
88+
int foo;
89+
}
90+
"""
91+
)
92+
);
93+
}
94+
95+
@Test
96+
void replaceHashCode() {
97+
rewriteRun(// language=java
98+
java(
99+
"""
100+
package com.example;
101+
102+
class A {
103+
104+
int foo;
105+
106+
@Override
107+
public int hashCode() {
108+
return 6;
109+
}
110+
}
111+
""",
112+
"""
113+
package com.example;
114+
115+
import lombok.EqualsAndHashCode;
116+
117+
@EqualsAndHashCode
118+
class A {
119+
120+
int foo;
121+
}
122+
"""
123+
)
124+
);
125+
}
126+
127+
@Test
128+
void replaceEqualsAndHashCode() {
129+
rewriteRun(// language=java
130+
java(
131+
"""
132+
package com.example;
133+
134+
class A {
135+
136+
int foo;
137+
138+
@Override
139+
public boolean equals(Object o) {
140+
return false;
141+
}
142+
143+
@Override
144+
public int hashCode() {
145+
return 6;
146+
}
147+
148+
}
149+
""",
150+
"""
151+
package com.example;
152+
153+
import lombok.EqualsAndHashCode;
154+
155+
@EqualsAndHashCode
156+
class A {
157+
158+
int foo;
159+
160+
}
161+
"""
162+
)
163+
);
164+
}
165+
166+
}

0 commit comments

Comments
 (0)