Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 71 additions & 53 deletions src/main/java/org/openrewrite/java/migrate/util/UseEnumSetOf.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*/
package org.openrewrite.java.migrate.util;

import lombok.EqualsAndHashCode;
import lombok.Value;
import org.jspecify.annotations.Nullable;
import org.openrewrite.*;
import org.openrewrite.java.JavaTemplate;
Expand All @@ -31,10 +33,21 @@
import java.util.List;
import java.util.StringJoiner;

@EqualsAndHashCode(callSuper = false)
@Value
public class UseEnumSetOf extends Recipe {
private static final MethodMatcher SET_OF = new MethodMatcher("java.util.Set of(..)", true);
private static final String METHOD_TYPE = "java.util.EnumSet";

@Option(
displayName = "Convert empty `Set.of()` to `EnumSet.noneOf()`",
description = "When true, converts `Set.of()` with no arguments to `EnumSet.noneOf()`. Default true.",
example = "true",
required = false
)
@Nullable
Boolean convertEmptySet;

@Override
public String getDisplayName() {
return "Prefer `EnumSet of(..)`";
Expand All @@ -52,73 +65,78 @@ public Duration getEstimatedEffortPerOccurrence() {

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return Preconditions.check(Preconditions.and(new UsesJavaVersion<>(9),
new UsesMethod<>(SET_OF)), new UseEnumSetOfVisitor());
}
return Preconditions.check(Preconditions.and(new UsesJavaVersion<>(9), new UsesMethod<>(SET_OF)), new JavaVisitor<ExecutionContext>() {
@Override
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation methodInvocation, ExecutionContext ctx) {
J.MethodInvocation mi = (J.MethodInvocation) super.visitMethodInvocation(methodInvocation, ctx);

private static class UseEnumSetOfVisitor extends JavaVisitor<ExecutionContext> {
@Override
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation methodInvocation, ExecutionContext ctx) {
J.MethodInvocation mi = (J.MethodInvocation) super.visitMethodInvocation(methodInvocation, ctx);

if (SET_OF.matches(mi) &&
mi.getType() instanceof JavaType.Parameterized &&
!TypeUtils.isOfClassType(mi.getType(), METHOD_TYPE)) {
Cursor parent = getCursor().dropParentUntil(is -> is instanceof J.Assignment || is instanceof J.VariableDeclarations || is instanceof J.Block);
if (!(parent.getValue() instanceof J.Block)) {
JavaType type = parent.getValue() instanceof J.Assignment ?
((J.Assignment) parent.getValue()).getType() : ((J.VariableDeclarations) parent.getValue()).getVariables().get(0).getType();
if (isAssignmentSetOfEnum(type)) {
maybeAddImport(METHOD_TYPE);

List<Expression> args = mi.getArguments();
if (isArrayParameter(args)) {
return mi;
}
if (SET_OF.matches(mi) &&
mi.getType() instanceof JavaType.Parameterized &&
!TypeUtils.isOfClassType(mi.getType(), METHOD_TYPE) &&
convertEmptySet(mi)) {
Cursor parent = getCursor().dropParentUntil(is -> is instanceof J.Assignment || is instanceof J.VariableDeclarations || is instanceof J.Block);
if (!(parent.getValue() instanceof J.Block)) {
JavaType type = parent.getValue() instanceof J.Assignment ?
((J.Assignment) parent.getValue()).getType() : ((J.VariableDeclarations) parent.getValue()).getVariables().get(0).getType();
if (isAssignmentSetOfEnum(type)) {
maybeAddImport(METHOD_TYPE);

if (args.get(0) instanceof J.Empty) {
JavaType firstTypeParameter = ((JavaType.Parameterized) type).getTypeParameters().get(0);
JavaType.ShallowClass shallowClass = JavaType.ShallowClass.build(firstTypeParameter.toString());
return JavaTemplate.builder("EnumSet.noneOf(" + shallowClass.getClassName() + ".class)")
List<Expression> args = mi.getArguments();
if (isArrayParameter(args)) {
return mi;
}

if (args.get(0) instanceof J.Empty) {
JavaType firstTypeParameter = ((JavaType.Parameterized) type).getTypeParameters().get(0);
JavaType.ShallowClass shallowClass = JavaType.ShallowClass.build(firstTypeParameter.toString());
return JavaTemplate.builder("EnumSet.noneOf(" + shallowClass.getClassName() + ".class)")
.contextSensitive()
.imports(METHOD_TYPE)
.build()
.apply(updateCursor(mi), mi.getCoordinates().replace());
}

StringJoiner setOf = new StringJoiner(", ", "EnumSet.of(", ")");
args.forEach(o -> setOf.add("#{any()}"));
return JavaTemplate.builder(setOf.toString())
.contextSensitive()
.imports(METHOD_TYPE)
.build()
.apply(updateCursor(mi), mi.getCoordinates().replace());
.apply(updateCursor(mi), mi.getCoordinates().replace(), args.toArray());
}

StringJoiner setOf = new StringJoiner(", ", "EnumSet.of(", ")");
args.forEach(o -> setOf.add("#{any()}"));
return JavaTemplate.builder(setOf.toString())
.contextSensitive()
.imports(METHOD_TYPE)
.build()
.apply(updateCursor(mi), mi.getCoordinates().replace(), args.toArray());
}
}
return mi;
}
return mi;
}

private boolean isAssignmentSetOfEnum(@Nullable JavaType type) {
if (type instanceof JavaType.Parameterized) {
JavaType.Parameterized parameterized = (JavaType.Parameterized) type;
if (TypeUtils.isOfClassType(parameterized.getType(), "java.util.Set")) {
return ((JavaType.Parameterized) type).getTypeParameters().stream()
.filter(org.openrewrite.java.tree.JavaType.Class.class::isInstance)
.map(org.openrewrite.java.tree.JavaType.Class.class::cast)
.anyMatch(o -> o.getKind() == JavaType.FullyQualified.Kind.Enum);

private boolean convertEmptySet(J.MethodInvocation mi) {
if (convertEmptySet == null || convertEmptySet) {
return true;
}
return !mi.getArguments().isEmpty() && !(mi.getArguments().get(0) instanceof J.Empty);
}
return false;
}

private boolean isArrayParameter(final List<Expression> args) {
if (args.size() != 1) {
private boolean isAssignmentSetOfEnum(@Nullable JavaType type) {
if (type instanceof JavaType.Parameterized) {
JavaType.Parameterized parameterized = (JavaType.Parameterized) type;
if (TypeUtils.isOfClassType(parameterized.getType(), "java.util.Set")) {
return ((JavaType.Parameterized) type).getTypeParameters().stream()
.filter(JavaType.Class.class::isInstance)
.map(JavaType.Class.class::cast)
.anyMatch(o -> o.getKind() == JavaType.FullyQualified.Kind.Enum);
}
}
return false;
}
JavaType type = args.get(0).getType();
return TypeUtils.asArray(type) != null;
}

private boolean isArrayParameter(final List<Expression> args) {
if (args.size() != 1) {
return false;
}
JavaType type = args.get(0).getType();
return TypeUtils.asArray(type) != null;
}
});
}

}
3 changes: 2 additions & 1 deletion src/main/resources/META-INF/rewrite/java-version-6.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ tags:
- java6
recipeList:
- org.openrewrite.java.migrate.jacoco.UpgradeJaCoCo
- org.openrewrite.java.migrate.util.UseEnumSetOf
- org.openrewrite.java.migrate.util.UseEnumSetOf:
convertEmptySet: false
- org.openrewrite.java.migrate.JREWrapperInterface
---
type: specs.openrewrite.org/v1beta/recipe
Expand Down
Loading