Skip to content

Commit 8b816f0

Browse files
ghustagithub-actions[bot]timtebeek
authored
Add recipe SimplifySingleElementAnnotation (#5740)
* Add recipe SimplifySingleElementAnnotation * Update rewrite-java/src/main/java/org/openrewrite/java/SimplifySingleElementAnnotation.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update rewrite-java/src/test/java/org/openrewrite/java/SimplifySingleElementAnnotationTest.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update rewrite-java/src/test/java/org/openrewrite/java/SimplifySingleElementAnnotationTest.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Add some more tests for special cases * Make exception for `J.Empty` * Simplify single attribute annotations are removing attribute --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Tim te Beek <timtebeek@gmail.com> Co-authored-by: Tim te Beek <tim@moderne.io>
1 parent 506c2d1 commit 8b816f0

File tree

5 files changed

+573
-1
lines changed

5 files changed

+573
-1
lines changed

rewrite-java-test/src/test/java/org/openrewrite/java/RemoveAnnotationAttributeTest.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,4 +87,34 @@ class A {}
8787
)
8888
);
8989
}
90+
91+
@Test
92+
void simplifyAfterRemoval() {
93+
rewriteRun(
94+
spec -> spec
95+
.parser(JavaParser.fromJavaVersion()
96+
.dependsOn(
97+
"""
98+
@interface Copyright {
99+
int year();
100+
String value();
101+
}
102+
"""
103+
)
104+
)
105+
.recipe(new RemoveAnnotationAttribute("Copyright", "year")),
106+
java(
107+
"""
108+
@Copyright(year = 2002, value = "Yoyodyne Propulsion Systems, Inc.")
109+
class OscillationOverthruster {
110+
}
111+
""",
112+
"""
113+
@Copyright("Yoyodyne Propulsion Systems, Inc.")
114+
class OscillationOverthruster {
115+
}
116+
"""
117+
)
118+
);
119+
}
90120
}

rewrite-java/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ dependencies {
7171
// For use in ClassGraphTypeMappingTest
7272
testRuntimeOnly("org.eclipse.persistence:org.eclipse.persistence.core:3.0.2")
7373
testRuntimeOnly("org.slf4j:jul-to-slf4j:1.7.+")
74+
testRuntimeOnly("jakarta.validation:jakarta.validation-api:3.1.1")
7475
}
7576

7677
tasks.withType<Javadoc>().configureEach {

rewrite-java/src/main/java/org/openrewrite/java/RemoveAnnotationAttribute.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public J.Annotation visitAnnotation(J.Annotation annotation, ExecutionContext ct
6868

6969
AtomicBoolean didPassFirstAttribute = new AtomicBoolean(false);
7070
AtomicBoolean shouldTrimNextPrefix = new AtomicBoolean(false);
71-
return a.withArguments(ListUtils.map(a.getArguments(), arg -> {
71+
J.Annotation withoutAttribute = a.withArguments(ListUtils.map(a.getArguments(), arg -> {
7272
try {
7373
if (arg instanceof J.Assignment) {
7474
J.Assignment assignment = (J.Assignment) arg;
@@ -96,6 +96,12 @@ public J.Annotation visitAnnotation(J.Annotation annotation, ExecutionContext ct
9696

9797
return arg;
9898
}));
99+
if (a != withoutAttribute &&
100+
withoutAttribute.getArguments() != null &&
101+
withoutAttribute.getArguments().size() == 1) {
102+
doAfterVisit(new SimplifySingleElementAnnotation().getVisitor());
103+
}
104+
return withoutAttribute;
99105
}
100106
});
101107
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright 2025 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;
17+
18+
import org.openrewrite.ExecutionContext;
19+
import org.openrewrite.Recipe;
20+
import org.openrewrite.TreeVisitor;
21+
import org.openrewrite.internal.ListUtils;
22+
import org.openrewrite.java.tree.Expression;
23+
import org.openrewrite.java.tree.J;
24+
import org.openrewrite.java.tree.Space;
25+
26+
import java.util.List;
27+
28+
public class SimplifySingleElementAnnotation extends Recipe {
29+
30+
@Override
31+
public String getDisplayName() {
32+
return "Simplify single-element annotation";
33+
}
34+
35+
@Override
36+
public String getDescription() {
37+
return "This recipe will remove the attribute `value` on single-element annotations. " +
38+
"According to JLS, a _single-element annotation_, is a shorthand designed for use with single-element annotation types.";
39+
}
40+
41+
@Override
42+
public TreeVisitor<?, ExecutionContext> getVisitor() {
43+
return new JavaIsoVisitor<ExecutionContext>() {
44+
@Override
45+
public J.Annotation visitAnnotation(J.Annotation annotation, ExecutionContext executionContext) {
46+
J.Annotation an = super.visitAnnotation(annotation, executionContext);
47+
48+
if (an.getArguments() != null && an.getArguments().size() == 1) {
49+
an = an.withArguments(ListUtils.mapFirst(an.getArguments(), v -> {
50+
if (v instanceof J.Assignment &&
51+
((J.Assignment) v).getVariable() instanceof J.Identifier &&
52+
"value".equals(((J.Identifier) ((J.Assignment) v).getVariable()).getSimpleName())) {
53+
Expression assignment = ((J.Assignment) v).getAssignment();
54+
if (assignment instanceof J.NewArray) {
55+
J.NewArray na = (J.NewArray) assignment;
56+
List<Expression> initializer = na.getInitializer();
57+
if (initializer != null && initializer.size() == 1 && !(initializer.get(0) instanceof J.Empty)) {
58+
return initializer.get(0).withPrefix(Space.EMPTY);
59+
}
60+
}
61+
return assignment.withPrefix(Space.EMPTY);
62+
}
63+
return v;
64+
}));
65+
}
66+
67+
return an;
68+
}
69+
};
70+
}
71+
72+
}

0 commit comments

Comments
 (0)