diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 59f7b83722f..936cc97e5c7 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,8 +1,8 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionSha256Sum=544c35d6bd849ae8a5ed0bcea39ba677dc40f49df7d1835561582da2009b961d +distributionSha256Sum=a4b4158601f8636cdeeab09bd76afb640030bb5b144aafe261a5e8af027dc612 diff --git a/gradlew b/gradlew index 1aa94a42690..b740cf13397 100755 --- a/gradlew +++ b/gradlew @@ -55,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. diff --git a/rewrite-core/src/main/java/org/openrewrite/Preconditions.java b/rewrite-core/src/main/java/org/openrewrite/Preconditions.java index c54e6e50c87..825f46886ff 100644 --- a/rewrite-core/src/main/java/org/openrewrite/Preconditions.java +++ b/rewrite-core/src/main/java/org/openrewrite/Preconditions.java @@ -15,6 +15,7 @@ */ package org.openrewrite; +import lombok.Getter; import org.openrewrite.internal.lang.Nullable; import org.openrewrite.marker.SearchResult; @@ -136,13 +137,15 @@ public RecipeCheck(Recipe check, TreeVisitor v) { this.check = check; } - public Recipe getCheck() { + public Recipe getRecipe() { return check; } } public static class Check extends TreeVisitor { + @Getter private final TreeVisitor check; + private final TreeVisitor v; public Check(TreeVisitor check, TreeVisitor v) { diff --git a/rewrite-gradle/src/main/java/org/openrewrite/gradle/UpgradeDependencyVersion.java b/rewrite-gradle/src/main/java/org/openrewrite/gradle/UpgradeDependencyVersion.java index 393bd36ef0e..14e39322551 100644 --- a/rewrite-gradle/src/main/java/org/openrewrite/gradle/UpgradeDependencyVersion.java +++ b/rewrite-gradle/src/main/java/org/openrewrite/gradle/UpgradeDependencyVersion.java @@ -139,7 +139,37 @@ public DependencyVersionState getInitialValue(ExecutionContext ctx) { return new DependencyVersionState(); } - private static final MethodMatcher DEPENDENCY_DSL_MATCHER = new MethodMatcher("DependencyHandlerSpec *(..)"); + private static final MethodMatcher DEPENDENCY_DSL_MATCHER = new MethodMatcher("RewriteGradleProject dependencies(groovy.lang.Closure)"); + private static final MethodMatcher DEPENDENCY_CONFIGURATION_MATCHER = new MethodMatcher("DependencyHandlerSpec *(..)"); + + private static boolean isLikelyDependencyConfiguration(Cursor cursor) { + if (!(cursor.getValue() instanceof J.MethodInvocation)) { + return false; + } + J.MethodInvocation m = cursor.getValue(); + if (DEPENDENCY_CONFIGURATION_MATCHER.matches(m)) { + return true; + } + // If it's a configuration created by a plugin, we may not be able to type-attribute it + // In the absence of type-attribution use its presence within a dependencies block to approximate + if (m.getType() != null) { + return false; + } + while (cursor != null) { + if (cursor.getValue() instanceof J.MethodInvocation) { + m = cursor.getValue(); + String methodName = m.getSimpleName(); + if ("constraints".equals(methodName)) { + return false; + } + if (DEPENDENCY_DSL_MATCHER.matches(m)) { + return true; + } + } + cursor = cursor.getParent(); + } + return false; + } @Override public TreeVisitor getScanner(DependencyVersionState acc) { @@ -159,7 +189,7 @@ public J visitCompilationUnit(G.CompilationUnit cu, ExecutionContext ctx) { @Override public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { J.MethodInvocation m = (J.MethodInvocation) super.visitMethodInvocation(method, ctx); - if (DEPENDENCY_DSL_MATCHER.matches(m)) { + if (isLikelyDependencyConfiguration(getCursor())) { if (m.getArguments().get(0) instanceof G.MapEntry) { String groupId = null; String artifactId = null; @@ -283,9 +313,10 @@ public TreeVisitor getVisitor(DependencyVersionState acc) { private class UpdateProperties extends PropertiesVisitor { final DependencyVersionState acc; final DependencyMatcher dependencyMatcher = new DependencyMatcher(groupId, artifactId, null); + @Override public Properties visitFile(Properties.File file, ExecutionContext ctx) { - if(!file.getSourcePath().endsWith(GRADLE_PROPERTIES_FILE_NAME)) { + if (!file.getSourcePath().endsWith(GRADLE_PROPERTIES_FILE_NAME)) { return file; } return super.visitFile(file, ctx); @@ -323,7 +354,7 @@ private class UpdateGroovy extends GroovyVisitor { public J visitCompilationUnit(G.CompilationUnit cu, ExecutionContext ctx) { gradleProject = cu.getMarkers().findFirst(GradleProject.class) .orElse(null); - if(gradleProject == null) { + if (gradleProject == null) { return cu; } return super.visitCompilationUnit(cu, ctx); @@ -352,12 +383,12 @@ public J postVisit(J tree, ExecutionContext ctx) { @Override public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { - if("constraints".equals(method.getSimpleName())) { + if ("constraints".equals(method.getSimpleName())) { // don't mess with anything inside a constraints block, leave that to UpgradeTransitiveDependency version recipe return method; } J.MethodInvocation m = (J.MethodInvocation) super.visitMethodInvocation(method, ctx); - if (DEPENDENCY_DSL_MATCHER.matches(m)) { + if (isLikelyDependencyConfiguration(getCursor())) { List depArgs = m.getArguments(); if (depArgs.get(0) instanceof J.Literal || depArgs.get(0) instanceof G.GString || depArgs.get(0) instanceof G.MapEntry) { m = updateDependency(m, ctx); diff --git a/rewrite-gradle/src/main/resources/gradlew b/rewrite-gradle/src/main/resources/gradlew index 1aa94a42690..b740cf13397 100755 --- a/rewrite-gradle/src/main/resources/gradlew +++ b/rewrite-gradle/src/main/resources/gradlew @@ -55,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. diff --git a/rewrite-gradle/src/test/java/org/openrewrite/gradle/UpgradeDependencyVersionTest.java b/rewrite-gradle/src/test/java/org/openrewrite/gradle/UpgradeDependencyVersionTest.java index a76d19a9c2e..a243296bf5c 100644 --- a/rewrite-gradle/src/test/java/org/openrewrite/gradle/UpgradeDependencyVersionTest.java +++ b/rewrite-gradle/src/test/java/org/openrewrite/gradle/UpgradeDependencyVersionTest.java @@ -855,4 +855,31 @@ void leaveConstraintsAlone() { ) ); } + + @Test + void unknownConfiguration() { + rewriteRun( + spec -> spec.recipe(new UpgradeDependencyVersion("org.openapitools", "openapi-generator-cli", "5.2.1", null)), + buildGradle( + """ + plugins { + id 'java' + id "org.hidetake.swagger.generator" version "2.18.2" + } + dependencies { + swaggerCodegen "org.openapitools:openapi-generator-cli:5.2.0" + } + """, + """ + plugins { + id 'java' + id "org.hidetake.swagger.generator" version "2.18.2" + } + dependencies { + swaggerCodegen "org.openapitools:openapi-generator-cli:5.2.1" + } + """ + ) + ); + } } diff --git a/rewrite-groovy/src/main/java/org/openrewrite/groovy/GroovyParserVisitor.java b/rewrite-groovy/src/main/java/org/openrewrite/groovy/GroovyParserVisitor.java index 29f574858f1..4e8c790df4c 100644 --- a/rewrite-groovy/src/main/java/org/openrewrite/groovy/GroovyParserVisitor.java +++ b/rewrite-groovy/src/main/java/org/openrewrite/groovy/GroovyParserVisitor.java @@ -1953,13 +1953,18 @@ public TypeTree visitVariableExpressionType(VariableExpression expression) { if (expression.isDynamicTyped()) { Space prefix = whitespace(); - String defOrVar = source.substring(cursor, cursor + 3); - cursor += 3; + String keyword; + if(source.substring(cursor).startsWith("final")) { + keyword = "final"; + } else { + keyword = source.substring(cursor, cursor + 3); + } + cursor += keyword.length(); return new J.Identifier(randomId(), prefix, Markers.EMPTY, emptyList(), - defOrVar, + keyword, type, null); } Space prefix = sourceBefore(expression.getOriginType().getUnresolvedName()); diff --git a/rewrite-groovy/src/test/java/org/openrewrite/groovy/tree/VariableDeclarationsTest.java b/rewrite-groovy/src/test/java/org/openrewrite/groovy/tree/VariableDeclarationsTest.java index bb6b8d56072..262aee3064f 100644 --- a/rewrite-groovy/src/test/java/org/openrewrite/groovy/tree/VariableDeclarationsTest.java +++ b/rewrite-groovy/src/test/java/org/openrewrite/groovy/tree/VariableDeclarationsTest.java @@ -42,6 +42,12 @@ void varKeyword() { ); } + @Test + void finalKeyword() { + rewriteRun( + groovy("final a = 1") + ); + } @Test void singleVariableDeclaration() { diff --git a/rewrite-java-test/src/test/java/org/openrewrite/java/ShortenFullyQualifiedTypeReferencesTest.java b/rewrite-java-test/src/test/java/org/openrewrite/java/ShortenFullyQualifiedTypeReferencesTest.java index e2f5c14626e..8b24193c880 100644 --- a/rewrite-java-test/src/test/java/org/openrewrite/java/ShortenFullyQualifiedTypeReferencesTest.java +++ b/rewrite-java-test/src/test/java/org/openrewrite/java/ShortenFullyQualifiedTypeReferencesTest.java @@ -42,14 +42,14 @@ void redundantImport() { java( """ import java.util.List; - + class T { java.util.List list; } """, """ import java.util.List; - + class T { List list; } @@ -70,7 +70,7 @@ class T { """, """ import java.util.Map; - + class T { Map.Entry mapEntry; } @@ -86,7 +86,7 @@ void conflictingTypeInSamePackage() { java( """ package a; - + class String { } """ @@ -95,7 +95,7 @@ class String { java( """ package a; - + class T { String s1; java.lang.String s2; @@ -117,7 +117,7 @@ class T { """, """ import java.util.regex.Pattern; - + class T { int dotall = Pattern.DOTALL; } @@ -133,14 +133,14 @@ void inGenericTypeParameter() { java( """ import java.util.List; - + class T { List> list; } """, """ import java.util.List; - + class T { List> list; } @@ -156,14 +156,14 @@ void equalType() { //language=java """ import java.util.List; - + class T { java.util.List list; } """, """ import java.util.List; - + class T { List list; } @@ -190,7 +190,7 @@ class T { """, """ import java.util.List; - + class T { List list; } @@ -212,7 +212,7 @@ class T { """, """ import java.util.List; - + class T { List list; java.awt.List list2; @@ -367,7 +367,7 @@ void conflictGenericVariable() { java( """ import java.util.List; - + class T { java.lang.String s; List list; @@ -385,7 +385,7 @@ void conflictGenericVariableOnMethod() { java( """ import java.util.List; - + class T { void m(java.lang.String s, List list) { } @@ -403,7 +403,7 @@ void qualifiedMethodReference() { """ import java.util.Collection; import java.util.function.Function; - + class T { Function, Integer> m() { return java.util.Collection::size; @@ -413,7 +413,7 @@ Function, Integer> m() { """ import java.util.Collection; import java.util.function.Function; - + class T { Function, Integer> m() { return Collection::size; @@ -440,7 +440,7 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, Ex """ import java.util.Collection; import java.util.function.Function; - + class T { Function, Integer> m() { return java.util.Collection::size; @@ -453,7 +453,7 @@ Function, Integer> m1() { """ import java.util.Collection; import java.util.function.Function; - + class T { Function, Integer> m() { return java.util.Collection::size; @@ -478,9 +478,9 @@ class A { } """), java( - """ + """ import java.util.ArrayList; - + class Test { void test(List.A l1) { java.util.List l2 = new ArrayList<>(); @@ -503,9 +503,9 @@ class B { } """), java( - """ + """ import java.util.ArrayList; - + class Test { void test(List.A.B l1) { java.util.List l2 = new ArrayList<>(); @@ -521,21 +521,21 @@ void importWithLeadingComment() { java( """ package foo; - + /* comment */ import java.util.List; - + class Test { List l = new java.util.ArrayList<>(); } """, """ package foo; - + /* comment */ import java.util.ArrayList; import java.util.List; - + class Test { List l = new ArrayList<>(); } @@ -552,7 +552,7 @@ void annotatedFieldAccess() { """ import java.lang.annotation.ElementType; import java.lang.annotation.Target; - + class Test { java.util. @Anno List l; } @@ -563,7 +563,7 @@ class Test { import java.lang.annotation.ElementType; import java.lang.annotation.Target; import java.util.List; - + class Test { @Anno List l; } @@ -583,10 +583,10 @@ void typeFullyQualifiedAnnotatedField() { import java.sql.DatabaseMetaData; import java.util.List; import java.lang.annotation.*; - + class TypeAnnotationTest { protected java.sql.@A DatabaseMetaData metadata; - + @Target({ElementType.FIELD, ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) private @interface A { } @@ -596,10 +596,10 @@ class TypeAnnotationTest { import java.sql.DatabaseMetaData; import java.util.List; import java.lang.annotation.*; - + class TypeAnnotationTest { protected @A DatabaseMetaData metadata; - + @Target({ElementType.FIELD, ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) private @interface A { } @@ -608,4 +608,26 @@ class TypeAnnotationTest { ) ); } + + @Test + void annotation() { + rewriteRun( + java( + """ + class TypeAnnotationTest { + @org.jetbrains.annotations.NotNull + String test() {} + } + """, + """ + import org.jetbrains.annotations.NotNull; + + class TypeAnnotationTest { + @NotNull + String test() {} + } + """ + ) + ); + } } diff --git a/rewrite-java/build.gradle.kts b/rewrite-java/build.gradle.kts index 7d0c6bea1c5..61318ccfa58 100644 --- a/rewrite-java/build.gradle.kts +++ b/rewrite-java/build.gradle.kts @@ -59,8 +59,10 @@ dependencies { // For use in ClassGraphTypeMappingTest testRuntimeOnly("org.eclipse.persistence:org.eclipse.persistence.core:3.0.2") - testRuntimeOnly("org.slf4j:jul-to-slf4j:1.7.+") + + // For use in ReplaceAnnotationTest + testRuntimeOnly("org.projectlombok:lombok:latest.release") } tasks.withType { diff --git a/rewrite-java/src/main/java/org/openrewrite/java/ReplaceAnnotation.java b/rewrite-java/src/main/java/org/openrewrite/java/ReplaceAnnotation.java new file mode 100644 index 00000000000..0534d699c50 --- /dev/null +++ b/rewrite-java/src/main/java/org/openrewrite/java/ReplaceAnnotation.java @@ -0,0 +1,110 @@ +/* + * Copyright 2024 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.openrewrite.java; + +import lombok.EqualsAndHashCode; +import lombok.Value; +import org.openrewrite.*; +import org.openrewrite.internal.lang.Nullable; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.JavaCoordinates; +import org.openrewrite.java.tree.JavaType; +import org.openrewrite.java.tree.TypeUtils; + +@Value +@EqualsAndHashCode(callSuper = false) +public class ReplaceAnnotation extends Recipe { + + @Option(displayName = "Annotation to replace", + description = "An annotation matcher, expressed as a method pattern to replace.", + example = "@org.jetbrains.annotations.NotNull(\"Test\")") + String annotationPatternToReplace; + + @Option(displayName = "Annotation template to insert", + description = "An annotation template to add instead of original one, will be parsed with `JavaTemplate`.", + example = "@org.jetbrains.annotations.NotNull(\"Null not permitted\")") + String annotationTemplateToInsert; + + @Option(displayName = "Classpath resource", + description = "If the annotation's type is defined by a jar within the META-INF/rewrite/classpath directory provide its name here " + + "so that it can be loaded. " + + "When this parameter is not passed the runtime classpath of the recipe is provided to the parser producing the new annotation. " + + "This is necessary when the annotation is not on the runtime classpath of the recipe and isn't in the Java standard library.", + required = false) + @Nullable + String classpathResourceName; + + @Override + public String getDisplayName() { + return "Replace Annotation"; + } + + @Override + public String getDescription() { + return "Replace an Annotation with another one if the annotation pattern matches. " + + "Only fixed parameters can be set in the replacement."; + } + + @Override + public TreeVisitor getVisitor() { + return new TreeVisitor() { + @Override + public @Nullable Tree visit(@Nullable Tree tree, ExecutionContext ctx) { + JavaTemplate.Builder templateBuilder = JavaTemplate.builder(annotationTemplateToInsert); + if (classpathResourceName == null) { + templateBuilder.javaParser(JavaParser.fromJavaVersion().classpath(JavaParser.runtimeClasspath())); + } else { + templateBuilder.javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, classpathResourceName)); + } + return new ReplaceAnnotationVisitor(new AnnotationMatcher(annotationPatternToReplace), templateBuilder.build()) + .visit(tree, ctx); + } + }; + } + + public static class ReplaceAnnotationVisitor extends JavaIsoVisitor { + private final AnnotationMatcher matcher; + private final JavaTemplate replacement; + + public ReplaceAnnotationVisitor(AnnotationMatcher annotationMatcher, JavaTemplate replacement) { + super(); + this.matcher = annotationMatcher; + this.replacement = replacement; + } + + @Override + public J.Annotation visitAnnotation(J.Annotation a, ExecutionContext ctx) { + J.Annotation maybeReplacingAnnotation = super.visitAnnotation(a, ctx); + + boolean keepAnnotation = !matcher.matches(maybeReplacingAnnotation); + if (keepAnnotation) { + return maybeReplacingAnnotation; + } + + + JavaType.FullyQualified replacedAnnotationType = TypeUtils.asFullyQualified(maybeReplacingAnnotation.getType()); + maybeRemoveImport(replacedAnnotationType); + + JavaCoordinates replaceCoordinate = maybeReplacingAnnotation.getCoordinates().replace(); + J.Annotation replacement = this.replacement.apply(getCursor(), replaceCoordinate); + + doAfterVisit(ShortenFullyQualifiedTypeReferences.modifyOnly(replacement)); + + return replacement; + } + } +} + diff --git a/rewrite-java/src/main/java/org/openrewrite/java/search/UsesMethod.java b/rewrite-java/src/main/java/org/openrewrite/java/search/UsesMethod.java index 8711ea076a9..3cdb078726d 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/search/UsesMethod.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/search/UsesMethod.java @@ -16,6 +16,7 @@ package org.openrewrite.java.search; import lombok.EqualsAndHashCode; +import lombok.Getter; import lombok.Value; import lombok.With; import org.openrewrite.Tree; @@ -34,6 +35,8 @@ public class UsesMethod

extends JavaIsoVisitor

{ private final String methodPattern; + + @Getter private final MethodMatcher methodMatcher; public UsesMethod(String methodPattern) { diff --git a/rewrite-java/src/main/java/org/openrewrite/java/search/UsesType.java b/rewrite-java/src/main/java/org/openrewrite/java/search/UsesType.java index fc785dfcade..e8a5fc0a20b 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/search/UsesType.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/search/UsesType.java @@ -15,6 +15,7 @@ */ package org.openrewrite.java.search; +import lombok.Getter; import org.openrewrite.Tree; import org.openrewrite.internal.StringUtils; import org.openrewrite.internal.lang.Nullable; @@ -33,8 +34,11 @@ public class UsesType

extends JavaIsoVisitor

{ @Nullable + @Getter private final String fullyQualifiedType; + @Nullable + @Getter private final Predicate typePattern; @Nullable diff --git a/rewrite-java/src/main/java/org/openrewrite/java/tree/JavaType.java b/rewrite-java/src/main/java/org/openrewrite/java/tree/JavaType.java index 905786a8643..e5b866e5ecd 100644 --- a/rewrite-java/src/main/java/org/openrewrite/java/tree/JavaType.java +++ b/rewrite-java/src/main/java/org/openrewrite/java/tree/JavaType.java @@ -657,7 +657,7 @@ public Parameterized(@Nullable Integer managedReference, @Nullable FullyQualifie } Parameterized(@Nullable Integer managedReference, @Nullable FullyQualified type, - @Nullable JavaType[] typeParameters) { + @Nullable JavaType[] typeParameters) { this.managedReference = managedReference; this.type = unknownIfNull(type); this.typeParameters = nullIfEmpty(typeParameters); @@ -1148,9 +1148,9 @@ public Method(@Nullable Integer managedReference, long flagsBitMap, @Nullable Fu } public Method(@Nullable Integer managedReference, long flagsBitMap, @Nullable FullyQualified declaringType, String name, - @Nullable JavaType returnType, @Nullable String[] parameterNames, - @Nullable JavaType[] parameterTypes, @Nullable FullyQualified[] thrownExceptions, - @Nullable FullyQualified[] annotations, @Nullable List defaultValue) { + @Nullable JavaType returnType, @Nullable String[] parameterNames, + @Nullable JavaType[] parameterTypes, @Nullable FullyQualified[] thrownExceptions, + @Nullable FullyQualified[] annotations, @Nullable List defaultValue) { this.managedReference = managedReference; this.flagsBitMap = flagsBitMap & Flag.VALID_FLAGS; this.declaringType = unknownIfNull(declaringType); @@ -1207,9 +1207,10 @@ public FullyQualified getDeclaringType() { return declaringType; } - public boolean isOverride() { + @Nullable + public JavaType.Method getOverride() { if (declaringType instanceof JavaType.Unknown) { - return false; + return null; } Stack interfaces = new Stack<>(); @@ -1231,11 +1232,15 @@ public boolean isOverride() { continue nextMethod; } } - return true; + return method; } } } - return false; + return null; + } + + public boolean isOverride() { + return getOverride() != null; } public boolean isInheritedFrom(String fullyQualifiedTypeName) { @@ -1397,7 +1402,7 @@ public Variable(@Nullable Integer managedReference, long flagsBitMap, String nam } Variable(@Nullable Integer managedReference, long flagsBitMap, String name, @Nullable JavaType owner, - @Nullable JavaType type, @Nullable FullyQualified[] annotations) { + @Nullable JavaType type, @Nullable FullyQualified[] annotations) { this.managedReference = managedReference; this.flagsBitMap = flagsBitMap & Flag.VALID_FLAGS; this.name = name; diff --git a/rewrite-java/src/test/java/org/openrewrite/java/ReplaceAnnotationTest.java b/rewrite-java/src/test/java/org/openrewrite/java/ReplaceAnnotationTest.java new file mode 100644 index 00000000000..894e5366777 --- /dev/null +++ b/rewrite-java/src/test/java/org/openrewrite/java/ReplaceAnnotationTest.java @@ -0,0 +1,142 @@ +/* + * Copyright 2024 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.openrewrite.java; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.openrewrite.DocumentExample; +import org.openrewrite.test.RewriteTest; + +import static org.openrewrite.java.Assertions.java; + +class ReplaceAnnotationTest implements RewriteTest { + + @Nested + class OnMatch { + @Test + void matchNoPrams() { + rewriteRun( + spec -> spec.recipe(new ReplaceAnnotation("@org.jetbrains.annotations.NotNull", "@lombok.NonNull", null)), + java( + """ + import org.jetbrains.annotations.NotNull; + + class A { + @NotNull + String testMethod() {} + } + """, + """ + import lombok.NonNull; + + class A { + @NonNull + String testMethod() {} + } + """ + ) + ); + } + + @Test + @DocumentExample + void matchWithPrams() { + rewriteRun( + spec -> spec.recipe(new ReplaceAnnotation("@org.jetbrains.annotations.NotNull(\"Test\")", "@lombok.NonNull", null)), + java( + """ + import org.jetbrains.annotations.NotNull; + + class A { + @NotNull("Test") + String testMethod() {} + } + """, + """ + import lombok.NonNull; + + class A { + @NonNull + String testMethod() {} + } + """ + ) + ); + } + + @Test + void insertWithParams() { + rewriteRun( + spec -> spec.recipe(new ReplaceAnnotation("@lombok.NonNull", "@org.jetbrains.annotations.NotNull(\"Test\")", null)), + java( + """ + import lombok.NonNull; + + class A { + @NonNull + String testMethod() {} + } + """, + """ + import org.jetbrains.annotations.NotNull; + + class A { + @NotNull("Test") + String testMethod() {} + } + """ + ) + ); + } + } + + @Nested + class NoMatch { + @Test + void noMatchOtherType() { + rewriteRun( + spec -> spec.recipe(new ReplaceAnnotation("@org.jetbrains.annotations.NotNull", "@lombok.NonNull", null)), + java( + """ + import org.jetbrains.annotations.Nullable; + + class A { + @Nullable("Test") + String testMethod() {} + } + """ + ) + ); + } + + @Test + void noMatchParameter() { + rewriteRun( + spec -> spec.recipe(new ReplaceAnnotation("@org.jetbrains.annotations.NotNull(\"Test\")", "@lombok.NonNull", null)), + java( + """ + import org.jetbrains.annotations.Nullable; + + class A { + @Nullable("Other") + String testMethod() {} + } + """ + ) + ); + } + } +}