Skip to content

Commit

Permalink
Fully remove part provider and last remaining references (#478)
Browse files Browse the repository at this point in the history
* Removed deprecated class references, replaced with new template. Need to test implementation.

* Update AddRouteTrailingSlash.java

* Minor fixes to AddRouteTrailingSlash

* Update ReplaceGlobalMethodSecurityWithMethodSecurity too

* Only use a single JavaTemplate

---------

Co-authored-by: Michel Gonzalez <[email protected]>
Co-authored-by: Tim te Beek <[email protected]>
  • Loading branch information
3 people authored Feb 10, 2024
1 parent d610f71 commit facf1c8
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 124 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,16 @@

import org.openrewrite.ExecutionContext;
import org.openrewrite.Recipe;
import org.openrewrite.Tree;
import org.openrewrite.TreeVisitor;
import org.openrewrite.internal.lang.Nullable;
import org.openrewrite.internal.ListUtils;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.PartProvider;
import org.openrewrite.java.tree.*;
import org.openrewrite.java.JavaTemplate;
import org.openrewrite.java.tree.Expression;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.Space;
import org.openrewrite.java.tree.TypeUtils;

import java.util.Collections;
import java.util.List;

import static java.util.Collections.emptyList;
import static org.openrewrite.java.tree.Space.EMPTY;

public class AddRouteTrailingSlash extends Recipe {
Expand All @@ -36,8 +36,6 @@ public class AddRouteTrailingSlash extends Recipe {
private static final String PUT_ANNOTATION_TYPE = "org.springframework.web.bind.annotation.PutMapping";
private static final String PATCH_ANNOTATION_TYPE = "org.springframework.web.bind.annotation.PatchMapping";
private static final String DELETE_ANNOTATION_TYPE = "org.springframework.web.bind.annotation.DeleteMapping";
@Nullable private static J.NewArray twoStringsArrayTemplate;
@Nullable private static J.Assignment valueAssignmentTemplate;

@Override
public String getDisplayName() {
Expand All @@ -57,7 +55,6 @@ public String getDescription() {
@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return new JavaIsoVisitor<ExecutionContext>() {

@Override
public J.Annotation visitAnnotation(J.Annotation annotation, ExecutionContext ctx) {
J.Annotation anno = super.visitAnnotation(annotation, ctx);
Expand All @@ -67,60 +64,61 @@ public J.Annotation visitAnnotation(J.Annotation annotation, ExecutionContext ct
return anno;
}

if (anno.getArguments().size() == 1 &&
isStringLiteral(anno.getArguments().get(0))) {

if (anno.getArguments().size() == 1 && isStringLiteral(anno.getArguments().get(0))) {
J.Literal str = (J.Literal) anno.getArguments().get(0);
if (!matchTrailingSlash(str.getValue().toString())) {
return annotation.withArguments(Collections.singletonList(buildTwoStringsArray(str)));
if (shouldAddTrailingSlashArgument(str.getValue().toString())) {
J.Annotation replacement = JavaTemplate.builder("{#{any(String)}, #{any(String)}}")
.build()
.apply(getCursor(),
anno.getCoordinates().replaceArguments(),
buildTwoStringsArray(str));
return autoFormat(replacement, ctx);
}
} else {
// search for value
List<Expression> args = anno.getArguments();
for (int i = 0; i < args.size(); i++) {
Expression exp = args.get(i);
// replace value
J.Annotation replacement = anno.withArguments(ListUtils.map(anno.getArguments(), exp -> {
if (exp instanceof J.Assignment) {
J.Assignment assignment = (J.Assignment) exp;
if (assignment.getVariable() instanceof J.Identifier &&
((J.Identifier) assignment.getVariable()).getSimpleName().equals("value") &&
isStringLiteral(assignment.getAssignment())) {

J.Literal str = (J.Literal) assignment.getAssignment();
if (!matchTrailingSlash(str.getValue().toString())) {
args.set(i, buildAssignment(str));
return annotation.withArguments(args);
if (shouldAddTrailingSlashArgument(str.getValue().toString())) {
return JavaTemplate.builder("value = {#{any(String)}, #{any(String)}}")
.contextSensitive()
.build()
.<J.Annotation>apply(getCursor(),
anno.getCoordinates().replaceArguments(),
buildTwoStringsArray(str)).getArguments().get(0);
}
}
}
}
return exp;
}));
return maybeAutoFormat(annotation, replacement, ctx);
}

return anno;
}
};
}

private boolean matchTrailingSlash(String str) {
return str.endsWith("/") || str.endsWith("*");
private boolean shouldAddTrailingSlashArgument(String str) {
return !str.endsWith("/") && !str.endsWith("*");
}

private J.NewArray buildTwoStringsArray(J.Literal path) {
private J[] buildTwoStringsArray(J.Literal path) {
String oriPath = path.getValue().toString();
String pathWithTrailingSlash = oriPath + '/';
J.NewArray twoPaths = getTwoStringsArrayTemplate();
List<Expression> exps = twoPaths.getInitializer();
exps.set(0, path.withPrefix(EMPTY));
exps.set(1, path.withValue(pathWithTrailingSlash)
.withValueSource("\"" + pathWithTrailingSlash + "\"")
.withPrefix(Space.build(" ", emptyList())));
return twoPaths.withInitializer(exps).withPrefix(EMPTY);
}

private J.Assignment buildAssignment(J.Literal path) {
J.NewArray twoPaths = buildTwoStringsArray(path);
return getAssignmentTemplate()
.withPrefix(Space.EMPTY)
.withAssignment(twoPaths.withPrefix(Space.build(" ", emptyList())));
return new J[]{
path.withId(Tree.randomId())
.withPrefix(EMPTY),
path.withId(Tree.randomId())
.withPrefix(Space.SINGLE_SPACE)
.withValue(pathWithTrailingSlash)
.withValueSource("\"" + pathWithTrailingSlash + "\"")
};
}

private static boolean isHttpVerbMappingAnnotation(String fqn) {
Expand All @@ -132,30 +130,6 @@ private static boolean isHttpVerbMappingAnnotation(String fqn) {
DELETE_ANNOTATION_TYPE.equals(fqn);
}

private static J.NewArray getTwoStringsArrayTemplate() {
if (twoStringsArrayTemplate == null) {
twoStringsArrayTemplate = PartProvider.buildPart(
"class Test {\n" +
" String[] value = { \"a\", \"b\"};\n" +
"}",
J.NewArray.class);
}
return twoStringsArrayTemplate;
}

private static J.Assignment getAssignmentTemplate() {
if (valueAssignmentTemplate == null) {
valueAssignmentTemplate = PartProvider.buildPart(
"class Test {\n" +
" void method() {\n" +
" String[] value;\n" +
" value = null;\n" +
" }\n" +
"}",
J.Assignment.class);
}
return valueAssignmentTemplate;
}

private static boolean isStringLiteral(Expression expression) {
return expression instanceof J.Literal && TypeUtils.isString(((J.Literal) expression).getType());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,15 @@
import org.openrewrite.Preconditions;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.internal.lang.Nullable;
import org.openrewrite.java.*;
import org.openrewrite.java.AnnotationMatcher;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.JavaParser;
import org.openrewrite.java.JavaTemplate;
import org.openrewrite.java.search.UsesType;
import org.openrewrite.java.spring.RemoveMethodInvocationsVisitor;
import org.openrewrite.java.tree.Expression;
import org.openrewrite.java.tree.J;

import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

Expand All @@ -36,8 +37,6 @@ public class ReplaceGlobalMethodSecurityWithMethodSecurity extends Recipe {

private static final String EnableGlobalMethodSecurityFqn = "org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity";
private static final String EnableMethodSecurityFqn = "org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity";
@Nullable
private static J.Assignment prePostEnabledToFalseAssignment;

@Override
public String getDisplayName() {
Expand All @@ -47,69 +46,54 @@ public String getDisplayName() {
@Override
public String getDescription() {
return "`@EnableGlobalMethodSecurity` and `<global-method-security>` are deprecated in favor of " +
"`@EnableMethodSecurity` and `<method-security>`, respectively. The new annotation and XML " +
"element activate Spring’s pre-post annotations by default and use AuthorizationManager internally.";
"`@EnableMethodSecurity` and `<method-security>`, respectively. The new annotation and XML " +
"element activate Spring’s pre-post annotations by default and use AuthorizationManager internally.";
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return Preconditions.check(new UsesType<>(
"org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity", false
), new JavaIsoVisitor<ExecutionContext>() {
@Override
public J.Annotation visitAnnotation(J.Annotation annotation, ExecutionContext ctx) {
annotation = super.visitAnnotation(annotation, ctx);
if (ENABLE_GLOBAL_METHOD_SECURITY_MATCHER.matches(annotation)) {
List<Expression> args = annotation.getArguments();
List<Expression> newArgs;
if (args != null && !args.isEmpty()) {
newArgs = args;
if (args.stream().noneMatch(this::hasPrePostEnabled)) {
newArgs.add(buildPrePostEnabledAssignedToFalse());
} else {
newArgs = args.stream().filter(arg -> !hasPrePostEnabled(arg)).collect(Collectors.toList());
return Preconditions.check(
new UsesType<>("org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity", false),
new JavaIsoVisitor<ExecutionContext>() {
@Override
public J.Annotation visitAnnotation(J.Annotation annotation, ExecutionContext ctx) {
annotation = super.visitAnnotation(annotation, ctx);
if (ENABLE_GLOBAL_METHOD_SECURITY_MATCHER.matches(annotation)) {
maybeAddImport(EnableMethodSecurityFqn);
maybeRemoveImport(EnableGlobalMethodSecurityFqn);
J.Annotation replacementAnnotation = JavaTemplate.builder("@EnableMethodSecurity(prePostEnabled = false)")
.javaParser(JavaParser.fromJavaVersion()
.classpathFromResources(ctx, "spring-security-config-5.8.+"))
.imports(EnableMethodSecurityFqn)
.build()
.apply(getCursor(), annotation.getCoordinates().replace());

List<Expression> oldArgs = annotation.getArguments();
if (oldArgs == null || oldArgs.isEmpty()) {
return replacementAnnotation;
}

List<Expression> newArgs = oldArgs;
if (oldArgs.stream().noneMatch(this::hasPrePostEnabled)) {
newArgs.add(replacementAnnotation.getArguments().get(0));
} else {
newArgs = oldArgs.stream().filter(arg -> !hasPrePostEnabled(arg)).collect(Collectors.toList());
}
return autoFormat(replacementAnnotation.withArguments(newArgs), ctx);
}
} else {
newArgs = Collections.singletonList(buildPrePostEnabledAssignedToFalse());
return annotation;
}

maybeAddImport(EnableMethodSecurityFqn);
maybeRemoveImport(EnableGlobalMethodSecurityFqn);
annotation = JavaTemplate.builder("@EnableMethodSecurity")
.javaParser(JavaParser.fromJavaVersion()
.classpathFromResources(ctx, "spring-security-config-5.8.+"))
.imports(EnableMethodSecurityFqn)
.build()
.apply(
getCursor(),
annotation.getCoordinates().replace()
);
return autoFormat(annotation.withArguments(newArgs), ctx);
}
return annotation;
}

private boolean hasPrePostEnabled(Expression arg) {
if (arg instanceof J.Assignment) {
J.Assignment assignment = (J.Assignment) arg;
return ((J.Identifier) assignment.getVariable()).getSimpleName().equals("prePostEnabled") &&
RemoveMethodInvocationsVisitor.isTrue(assignment.getAssignment());
private boolean hasPrePostEnabled(Expression arg) {
if (arg instanceof J.Assignment) {
J.Assignment assignment = (J.Assignment) arg;
return ((J.Identifier) assignment.getVariable()).getSimpleName().equals("prePostEnabled") &&
RemoveMethodInvocationsVisitor.isTrue(assignment.getAssignment());
}
return false;
}
}
return false;
}
});
);
}

private static J.Assignment buildPrePostEnabledAssignedToFalse() {
if (prePostEnabledToFalseAssignment == null) {
prePostEnabledToFalseAssignment = PartProvider.buildPart(
"import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;\n" +
"@EnableMethodSecurity(prePostEnabled = false)\n" +
"public class config {}",
J.Assignment.class,
"spring-security-config-5.8.+"
);
}
return prePostEnabledToFalseAssignment;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public void defaults(RecipeSpec spec) {
@Test
void simpleCase() {
rewriteRun(
//language=java
java(
"""
import org.springframework.web.bind.annotation.*;
Expand Down Expand Up @@ -68,6 +69,7 @@ public String getExample() {
@Test
void doNotChangeIfWithTrailingSlash() {
rewriteRun(
//language=java
java(
"""
import org.springframework.web.bind.annotation.*;
Expand All @@ -93,6 +95,7 @@ public String requestExample() {
@Test
void doNotChangeWithWildcard() {
rewriteRun(
//language=java
java(
"""
import org.springframework.web.bind.annotation.*;
Expand All @@ -113,6 +116,7 @@ public String getExample() {
@Test
void allSixKindHttpVerbMappings() {
rewriteRun(
//language=java
java(
"""
import org.springframework.web.bind.annotation.*;
Expand Down Expand Up @@ -187,6 +191,7 @@ public String deleteExample() {
@Test
void mappingHasValue() {
rewriteRun(
//language=java
java(
"""
import org.springframework.web.bind.annotation.*;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public void defaults(RecipeSpec spec) {
@Test
void replaceWithPrePostEnabled() {
rewriteRun(
//language=java
java(
"""
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
Expand All @@ -62,6 +63,7 @@ public class config {
@Test
void emptyAnnotation() {
rewriteRun(
//language=java
java(
"""
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
Expand All @@ -84,6 +86,7 @@ public class config {
@Test
void replaceWithNotPrePostEnabled() {
rewriteRun(
//language=java
java(
"""
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
Expand Down

0 comments on commit facf1c8

Please sign in to comment.