Skip to content

Commit fe32354

Browse files
authored
Switch case add enum values as guards
1 parent 9071333 commit fe32354

File tree

3 files changed

+294
-0
lines changed

3 files changed

+294
-0
lines changed
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
* <p>
4+
* Licensed under the Moderne Source Available License (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://docs.moderne.io/licensing/moderne-source-available-license
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.lang;
17+
18+
import lombok.EqualsAndHashCode;
19+
import lombok.Value;
20+
import org.jspecify.annotations.Nullable;
21+
import org.openrewrite.ExecutionContext;
22+
import org.openrewrite.Preconditions;
23+
import org.openrewrite.Recipe;
24+
import org.openrewrite.TreeVisitor;
25+
import org.openrewrite.java.JavaIsoVisitor;
26+
import org.openrewrite.java.JavaVisitor;
27+
import org.openrewrite.java.tree.Expression;
28+
import org.openrewrite.java.tree.J;
29+
import org.openrewrite.java.tree.JavaType;
30+
import org.openrewrite.java.tree.TypeUtils;
31+
import org.openrewrite.staticanalysis.kotlin.KotlinFileChecker;
32+
33+
import static java.util.Collections.singletonList;
34+
35+
@Value
36+
@EqualsAndHashCode(callSuper = false)
37+
public class SwitchCaseEnumGuardToLabel extends Recipe {
38+
@Override
39+
public String getDisplayName() {
40+
return "Use switch cases labels for enums";
41+
}
42+
43+
@Override
44+
public String getDescription() {
45+
return "Use switch case labels when a guard is checking equality with an enum.";
46+
}
47+
48+
@Override
49+
public TreeVisitor<?, ExecutionContext> getVisitor() {
50+
return Preconditions.check(Preconditions.not(new KotlinFileChecker<>()), new JavaIsoVisitor<ExecutionContext>() {
51+
52+
@Override
53+
public J.Case visitCase(J.Case case_, ExecutionContext ctx) {
54+
J.Case visitedCase = super.visitCase(case_, ctx);
55+
56+
J.VariableDeclarations.NamedVariable label = getCreatedLabelVariable(visitedCase);
57+
if (label == null) {
58+
return visitedCase;
59+
}
60+
61+
JavaType type = label.getType();
62+
if (type instanceof JavaType.Class && ((JavaType.Class) type).getKind() == JavaType.FullyQualified.Kind.Enum) {
63+
Expression guardedEnum = getGuardedEnum(visitedCase, label);
64+
if (guardedEnum != null) {
65+
J modifiedBody = enumReferencesToEnumValue(label.getSimpleName(), guardedEnum)
66+
.visit(visitedCase.getBody(), ctx);
67+
return visitedCase.withGuard(null)
68+
.withCaseLabels(singletonList(guardedEnum.withPrefix(visitedCase.getCaseLabels().get(0).getPrefix())))
69+
.withBody(modifiedBody);
70+
}
71+
}
72+
return visitedCase;
73+
}
74+
75+
private J.VariableDeclarations.@Nullable NamedVariable getCreatedLabelVariable(J.Case case_) {
76+
if (case_.getCaseLabels().size() != 1 || !(case_.getCaseLabels().get(0) instanceof J.VariableDeclarations)) {
77+
return null;
78+
}
79+
J.VariableDeclarations decl = (J.VariableDeclarations) case_.getCaseLabels().get(0);
80+
if (decl.getVariables().size() != 1) {
81+
return null;
82+
}
83+
return decl.getVariables().get(0);
84+
}
85+
86+
private @Nullable Expression getGuardedEnum(J.Case case_, J.VariableDeclarations.NamedVariable label) {
87+
Expression guard = case_.getGuard();
88+
if (guard == null) {
89+
return null;
90+
}
91+
Expression select = null;
92+
Expression equalTo = null;
93+
if (guard instanceof J.MethodInvocation) {
94+
J.MethodInvocation methodGuard = (J.MethodInvocation) guard;
95+
if ("equals".equals(methodGuard.getSimpleName()) && methodGuard.getArguments().size() == 1) {
96+
select = methodGuard.getSelect();
97+
equalTo = methodGuard.getArguments().get(0);
98+
}
99+
} else if (guard instanceof J.Binary) {
100+
J.Binary binaryGuard = (J.Binary) guard;
101+
if (J.Binary.Type.Equal == binaryGuard.getOperator()) {
102+
select = binaryGuard.getLeft();
103+
equalTo = binaryGuard.getRight();
104+
}
105+
}
106+
if ((select instanceof J.FieldAccess || select instanceof J.Identifier) && equalTo instanceof J.Identifier && label.getName().getSimpleName().equals(((J.Identifier) equalTo).getSimpleName())) {
107+
return select;
108+
} else if ((equalTo instanceof J.FieldAccess || equalTo instanceof J.Identifier) && select instanceof J.Identifier && label.getName().getSimpleName().equals(((J.Identifier) select).getSimpleName())) {
109+
return equalTo;
110+
}
111+
return null;
112+
}
113+
114+
private JavaVisitor<ExecutionContext> enumReferencesToEnumValue(String name, Expression enumReference) {
115+
return new JavaVisitor<ExecutionContext>() {
116+
@Override
117+
public J visitIdentifier(J.Identifier ident, ExecutionContext ctx) {
118+
J.Identifier identifier = (J.Identifier) super.visitIdentifier(ident, ctx);
119+
if (identifier.getSimpleName().equals(name) && TypeUtils.isOfType(identifier.getType(), enumReference.getType())) {
120+
return enumReference.withPrefix(identifier.getPrefix());
121+
}
122+
return identifier;
123+
}
124+
};
125+
}
126+
});
127+
}
128+
}

src/main/resources/META-INF/rewrite/java-version-21.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ recipeList:
3939
- org.openrewrite.java.migrate.UpgradePluginsForJava21
4040
- org.openrewrite.java.migrate.DeleteDeprecatedFinalize
4141
- org.openrewrite.java.migrate.RemovedSubjectMethods
42+
- org.openrewrite.java.migrate.SwitchPatternMatching
4243

4344
---
4445
type: specs.openrewrite.org/v1beta/recipe
@@ -130,3 +131,12 @@ recipeList:
130131
methodPattern: java.awt.image.ColorModel finalize()
131132
- org.openrewrite.java.RemoveMethodInvocations:
132133
methodPattern: java.awt.image.IndexColorModel finalize()
134+
---
135+
type: specs.openrewrite.org/v1beta/recipe
136+
name: org.openrewrite.java.migrate.SwitchPatternMatching
137+
displayName: Adopt switch pattern matching (JEP 441)
138+
description: "[JEP 441](https://openjdk.org/jeps/441) describes how some switch statements can be improved with pattern matching."
139+
tags:
140+
- java21
141+
recipeList:
142+
- org.openrewrite.java.migrate.lang.SwitchCaseEnumGuardToLabel
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
* <p>
4+
* Licensed under the Moderne Source Available License (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://docs.moderne.io/licensing/moderne-source-available-license
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.lang;
17+
18+
import org.junit.jupiter.api.Test;
19+
import org.openrewrite.DocumentExample;
20+
import org.openrewrite.test.RecipeSpec;
21+
import org.openrewrite.test.RewriteTest;
22+
23+
import static org.openrewrite.java.Assertions.java;
24+
25+
class SwitchCaseEnumGuardToLabelTest implements RewriteTest {
26+
@Override
27+
public void defaults(RecipeSpec spec) {
28+
spec.recipe(new SwitchCaseEnumGuardToLabel());
29+
}
30+
31+
@Test
32+
@DocumentExample
33+
void enumValuesPatternMatching() {
34+
rewriteRun(
35+
java(
36+
"""
37+
package suits;
38+
public enum Suit {
39+
CLUBS, DIAMONDS, HEARTS, SPADES, JOKER, SCORECARD
40+
}
41+
"""
42+
),
43+
//language=java
44+
java(
45+
"""
46+
import suits.Suit;
47+
class Test {
48+
void score(Object obj) {
49+
switch (obj) {
50+
case null -> System.out.println("You did not enter the test yet");
51+
case Suit s when s == Suit.CLUBS -> System.out.println("Clubs");
52+
case Suit s when s.equals(Suit.DIAMONDS) -> System.out.println("Diamonds");
53+
case Suit s when Suit.HEARTS.equals(s) -> {
54+
System.out.println("Hearts");
55+
}
56+
case Suit s when Suit.SPADES == s -> System.out.println("Spades");
57+
case Suit s when Suit.JOKER == s -> System.out.println(s);
58+
case Integer i -> System.out.println("Sorry?");
59+
case String s -> System.out.println("Sorry?");
60+
default -> System.out.println("Sorry?");
61+
}
62+
}
63+
}
64+
""",
65+
"""
66+
import suits.Suit;
67+
class Test {
68+
void score(Object obj) {
69+
switch (obj) {
70+
case null -> System.out.println("You did not enter the test yet");
71+
case Suit.CLUBS -> System.out.println("Clubs");
72+
case Suit.DIAMONDS -> System.out.println("Diamonds");
73+
case Suit.HEARTS -> {
74+
System.out.println("Hearts");
75+
}
76+
case Suit.SPADES -> System.out.println("Spades");
77+
case Suit.JOKER -> System.out.println(Suit.JOKER);
78+
case Integer i -> System.out.println("Sorry?");
79+
case String s -> System.out.println("Sorry?");
80+
default -> System.out.println("Sorry?");
81+
}
82+
}
83+
}
84+
"""
85+
)
86+
);
87+
}
88+
89+
@Test
90+
void staticImportedEnumValue() {
91+
rewriteRun(
92+
java(
93+
"""
94+
package suits;
95+
public enum Suit {
96+
CLUBS, DIAMONDS, HEARTS, SPADES, JOKER
97+
}
98+
"""
99+
),
100+
//language=java
101+
java(
102+
"""
103+
import suits.Suit;
104+
import static suits.Suit.JOKER;
105+
class Test {
106+
void score(Object obj) {
107+
switch (obj) {
108+
case Suit s when JOKER == s -> System.out.println(s);
109+
}
110+
}
111+
}
112+
""",
113+
"""
114+
import suits.Suit;
115+
import static suits.Suit.JOKER;
116+
class Test {
117+
void score(Object obj) {
118+
switch (obj) {
119+
case JOKER -> System.out.println(JOKER);
120+
}
121+
}
122+
}
123+
"""
124+
)
125+
);
126+
}
127+
128+
@Test
129+
void noChangeWithIntendedUse() {
130+
rewriteRun(
131+
java(
132+
"""
133+
package suits;
134+
public enum Suit {
135+
CLUBS, DIAMONDS, HEARTS, SPADES, JOKER
136+
}
137+
"""
138+
),
139+
//language=java
140+
java(
141+
"""
142+
import suits.Suit;
143+
import static suits.Suit.HEARTS;
144+
class Test {
145+
void score(Object obj) {
146+
switch (obj) {
147+
case Suit.SPADES -> System.out.println(Suit.SPADES);
148+
case HEARTS -> System.out.println(HEARTS);
149+
}
150+
}
151+
}
152+
"""
153+
)
154+
);
155+
}
156+
}

0 commit comments

Comments
 (0)