Skip to content

Migrate JAX-RS default value annotation #604

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public String getHelloWorldJSON(@PathVariable("name") String name) throws Except
}

@RequestMapping(value = "/json", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
public String getAllPersons(@RequestParam(required = false, value = "q") String searchBy) throws Exception {
public String getAllPersons(@RequestParam(required = false, value = "q") String searchBy, @RequestParam(required = false, defaultValue = "0", value = "page") int page) throws Exception {
return "{\\"message\\":\\"No person here...\\"";
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public String getHelloWorldJSON(@PathParam("name") String name) throws Exception
@Path("/json")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public String getAllPersons(@QueryParam("q") String searchBy) throws Exception {
public String getAllPersons(@QueryParam("q") String searchBy, @DefaultValue("0") @QueryParam("page") int page) throws Exception {
return "{\"message\":\"No person here...\"";
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
*/
package org.springframework.sbm.jee.jaxrs;

import lombok.RequiredArgsConstructor;
import org.openrewrite.java.ChangeType;
import org.openrewrite.java.JavaParser;
import org.springframework.context.annotation.Bean;
Expand All @@ -35,10 +34,10 @@
import org.springframework.sbm.java.migration.conditions.HasTypeAnnotation;
import org.springframework.sbm.jee.jaxrs.actions.ConvertJaxRsAnnotations;
import org.springframework.sbm.jee.jaxrs.recipes.ReplaceMediaType;
import org.springframework.sbm.jee.jaxrs.recipes.ReplaceRequestParameterProperties;
import org.springframework.sbm.jee.jaxrs.recipes.SwapCacheControl;
import org.springframework.sbm.jee.jaxrs.recipes.SwapHttHeaders;
import org.springframework.sbm.jee.jaxrs.recipes.SwapResponseWithResponseEntity;
import org.springframework.sbm.support.openrewrite.java.AddOrReplaceAnnotationAttribute;

import java.util.List;
import java.util.function.Supplier;
Expand Down Expand Up @@ -128,6 +127,12 @@ public Recipe jaxRs(RewriteRecipeLoader rewriteRecipeLoader, RewriteRecipeRunner
.recipe(new SwapResponseWithResponseEntity(javaParserSupplier))
.build(),

JavaRecipeAction.builder()
.condition(HasAnnotation.builder().annotation("org.springframework.web.bind.annotation.RequestParam").build())
.description("Replace the JAX-RS properties of a request parameter (like default value) with it's Spring equivalent.")
.recipe(new ReplaceRequestParameterProperties())
.build(),

OpenRewriteDeclarativeRecipeAdapter.builder()
.condition(HasAnnotation.builder().annotation("org.springframework.web.bind.annotation.RequestParam").build())
.description("Adds required=false to all @RequestParam annotations")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright 2021 - 2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.sbm.jee.jaxrs.recipes;

import lombok.EqualsAndHashCode;
import lombok.Value;
import org.jetbrains.annotations.NotNull;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Option;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.search.UsesType;
import org.springframework.sbm.jee.jaxrs.recipes.visitors.CopyAnnotationAttributeVisitor;

/**
* @author Vincent Botteman
*/
@Value
@EqualsAndHashCode(callSuper = true)
public class CopyAnnotationAttribute extends Recipe {
@Option(displayName = "Source Annotation Type",
description = "The fully qualified name of the source annotation.",
example = "org.junit.Test")
String sourceAnnotationType;

@Option(displayName = "Source Attribute name",
description = "The name of the attribute on the source annotation containing the value to copy.",
example = "timeout")
String sourceAttributeName;

@Option(displayName = "Target Annotation Type",
description = "The fully qualified name of the target annotation.",
example = "org.junit.Test")
String targetAnnotationType;

@Option(displayName = "Target Attribute name",
description = "The name of the attribute on the target annotation which must be set to the value of the source attribute.",
example = "timeout")
String targetAttributeName;

@Override
protected TreeVisitor<?, ExecutionContext> getSingleSourceApplicableTest() {
return new UsesType<>(sourceAnnotationType);
}

@Override
public @NotNull String getDisplayName() {
return "Copy an annotation attribute to another annotation";
}

@Override
public @NotNull String getDescription() {
return "Copy the value of an annotation attribute to another annotation attribute.";
}

@Override
protected @NotNull JavaIsoVisitor<ExecutionContext> getVisitor() {
return new CopyAnnotationAttributeVisitor(
sourceAnnotationType,
sourceAttributeName,
targetAnnotationType,
targetAttributeName
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright 2021 - 2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.sbm.jee.jaxrs.recipes;

import lombok.EqualsAndHashCode;
import lombok.Value;
import org.jetbrains.annotations.NotNull;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Option;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.java.search.UsesType;
import org.springframework.sbm.jee.jaxrs.recipes.visitors.RemoveAnnotationIfAccompaniedVisitor;

/**
* @author Vincent Botteman
*/
@EqualsAndHashCode(callSuper = true)
@Value
public class RemoveAnnotationIfAccompanied extends Recipe {
@Option(displayName = "Annotation Type to remove",
description = "The fully qualified name of the annotation to remove.",
example = "org.junit.Test")
String annotationTypeToRemove;

@Option(displayName = "Annotation Type which must also be present",
description = "The fully qualified name of the annotation that must also be present.",
example = "org.junit.Test")
String additionalAnnotationType;

@Override
public @NotNull String getDisplayName() {
return "Remove annotation if accompanied by the other annotation";
}

@Override
public @NotNull String getDescription() {
return "Remove matching annotation if the other annotation is also present.";
}

@Override
protected TreeVisitor<?, ExecutionContext> getSingleSourceApplicableTest() {
return new UsesType<>(annotationTypeToRemove);
}

@Override
public @NotNull RemoveAnnotationIfAccompaniedVisitor getVisitor() {
return new RemoveAnnotationIfAccompaniedVisitor(annotationTypeToRemove, additionalAnnotationType);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright 2021 - 2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.sbm.jee.jaxrs.recipes;

import org.jetbrains.annotations.NotNull;
import org.openrewrite.Recipe;

/**
* @author Vincent Botteman
*/
public class ReplaceRequestParameterProperties extends Recipe {
public ReplaceRequestParameterProperties() {
doNext(new CopyAnnotationAttribute(
"javax.ws.rs.DefaultValue", "value", "org.springframework.web.bind.annotation.RequestParam", "defaultValue")
);
doNext(new RemoveAnnotationIfAccompanied(
"javax.ws.rs.DefaultValue", "org.springframework.web.bind.annotation.RequestParam"
));
}

@Override
public @NotNull String getDisplayName() {
return "Migrate the properties of a request parameter: default value, ...";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* Copyright 2021 - 2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.sbm.jee.jaxrs.recipes.visitors;

import lombok.EqualsAndHashCode;
import lombok.Value;
import org.jetbrains.annotations.NotNull;
import org.openrewrite.Cursor;
import org.openrewrite.ExecutionContext;
import org.openrewrite.java.AddOrUpdateAnnotationAttribute;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.JavaType;
import org.openrewrite.java.tree.TypeUtils;
import org.springframework.sbm.jee.utils.AnnotationUtils;

import java.util.Optional;

/**
* @author Vincent Botteman
*/
@Value
@EqualsAndHashCode(callSuper = true)
public class CopyAnnotationAttributeVisitor extends JavaIsoVisitor<ExecutionContext> {
String sourceAnnotationType;
String sourceAttributeName;
String targetAnnotationType;
String targetAttributeName;

@Override
public J.Annotation visitAnnotation(@NotNull J.Annotation annotation, @NotNull ExecutionContext ctx) {
J.Annotation a = super.visitAnnotation(annotation, ctx);

if (!TypeUtils.isOfClassType(a.getType(), targetAnnotationType)) {
return a;
}

Cursor parent = getCursor().getParent();
if (parent == null) {
return a;
}
J.VariableDeclarations variableDeclaration = parent.getValue();
Optional<J.Literal> optionalSourceAnnotationAttributeValue = getSourceAnnotationAttributeValue(variableDeclaration);
if (optionalSourceAnnotationAttributeValue.isEmpty()) {
return a;
}

J.Literal sourceAnnotationAttributeValue = optionalSourceAnnotationAttributeValue.get();
if (sourceAnnotationAttributeValue.getValue() != null) {
// If the annotation type is a shallow class then JavaType.getMethods is empty and AddOrUpdateAnnotationAttribute can't determine if the datatype of the attribute is String or not
String targetAttributeValue = annotation.getType() instanceof JavaType.ShallowClass ? sourceAnnotationAttributeValue.getValueSource() : sourceAnnotationAttributeValue.getValue().toString();
JavaIsoVisitor<ExecutionContext> addOrUpdateAnnotationAttributeVisitor = new AddOrUpdateAnnotationAttribute(targetAnnotationType, targetAttributeName, targetAttributeValue, false)
.getVisitor();
if (targetAnnotationOnlyHasOneLiteralArgument(a)) {
a = (J.Annotation) addOrUpdateAnnotationAttributeVisitor.visit(a, ctx, getCursor());
}
return (J.Annotation) addOrUpdateAnnotationAttributeVisitor.visit(a, ctx, getCursor());
}
return a;
}

private Optional<J.Literal> getSourceAnnotationAttributeValue(J.VariableDeclarations methodParameterDeclaration) {
return methodParameterDeclaration.getLeadingAnnotations().stream()
.filter(annotation -> TypeUtils.isOfClassType(annotation.getType(), sourceAnnotationType))
.flatMap(annotation -> AnnotationUtils.getAttributeValue(annotation, sourceAttributeName).stream())
.findAny();
}

private boolean targetAnnotationOnlyHasOneLiteralArgument(@NotNull J.Annotation annotation) {
return annotation.getArguments() != null && annotation.getArguments().size() == 1 && annotation.getArguments().get(0) instanceof J.Literal;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright 2021 - 2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.sbm.jee.jaxrs.recipes.visitors;

import lombok.EqualsAndHashCode;
import lombok.Value;
import org.jetbrains.annotations.NotNull;
import org.openrewrite.ExecutionContext;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.RemoveAnnotation;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.JavaType;
import org.openrewrite.java.tree.TypeUtils;

/**
* @author Vincent Botteman
*/
@Value
@EqualsAndHashCode(callSuper = true)
public class RemoveAnnotationIfAccompaniedVisitor extends JavaIsoVisitor<ExecutionContext> {
private static final String ANNOTATION_REMOVED_KEY = "annotationRemoved";
String annotationTypeToRemove;
String additionalAnnotationType;

@Override
public J.VariableDeclarations visitVariableDeclarations(@NotNull J.VariableDeclarations multiVariable, @NotNull ExecutionContext ctx) {
J.VariableDeclarations m = super.visitVariableDeclarations(multiVariable, ctx);

if (variableDeclarationContainsAnnotationType(m, annotationTypeToRemove) && variableDeclarationContainsAnnotationType(m, additionalAnnotationType)) {
JavaIsoVisitor<ExecutionContext> removeAnnotationVisitor = new RemoveAnnotation("@" + annotationTypeToRemove)
.getVisitor();
m = (J.VariableDeclarations) removeAnnotationVisitor.visit(m, ctx, getCursor());
this.maybeRemoveImport(TypeUtils.asFullyQualified(JavaType.buildType(annotationTypeToRemove)));
}

return m;
}

private boolean variableDeclarationContainsAnnotationType(J.VariableDeclarations variableDeclaration, String annotationType) {
return variableDeclaration.getLeadingAnnotations().stream().anyMatch(annotation -> TypeUtils.isOfClassType(annotation.getType(), annotationType));
}
}
Loading