From 663e0beaed7c0f26f49d5cd8df334263c70f6fbb Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Thu, 13 Aug 2026 19:35:37 +0200 Subject: [PATCH 1/3] Do not duplicate JSpecify annotations when several annotations map to one When an element carries two nullability annotations that both migrate to the same JSpecify annotation -- such as `javax.annotation.Nullable` together with `javax.annotation.CheckForNull`, or annotations from two different frameworks -- the `ChangeType` steps each rewrite their own annotation, leaving the element annotated twice. Add a `RemoveDuplicateAnnotations` recipe that drops repeated annotations of the same type, and run it at the end of each `MigrateFrom*` recipe. Fixes #1199 --- .../jspecify/RemoveDuplicateAnnotations.java | 120 ++++++++++ .../resources/META-INF/rewrite/jspecify.yml | 12 + .../resources/META-INF/rewrite/recipes.csv | 27 +-- .../jspecify/JSpecifyBestPracticesTest.java | 34 +++ .../RemoveDuplicateAnnotationsTest.java | 220 ++++++++++++++++++ 5 files changed, 400 insertions(+), 13 deletions(-) create mode 100644 src/main/java/org/openrewrite/java/migrate/jspecify/RemoveDuplicateAnnotations.java create mode 100644 src/test/java/org/openrewrite/java/migrate/jspecify/RemoveDuplicateAnnotationsTest.java diff --git a/src/main/java/org/openrewrite/java/migrate/jspecify/RemoveDuplicateAnnotations.java b/src/main/java/org/openrewrite/java/migrate/jspecify/RemoveDuplicateAnnotations.java new file mode 100644 index 0000000000..ed9a6232a4 --- /dev/null +++ b/src/main/java/org/openrewrite/java/migrate/jspecify/RemoveDuplicateAnnotations.java @@ -0,0 +1,120 @@ +/* + * Copyright 2026 the original author or authors. + *

+ * Licensed under the Moderne Source Available License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * https://docs.moderne.io/licensing/moderne-source-available-license + *

+ * 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.migrate.jspecify; + +import lombok.EqualsAndHashCode; +import lombok.Value; +import org.openrewrite.*; +import org.openrewrite.internal.ListUtils; +import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.TypeMatcher; +import org.openrewrite.java.search.SemanticallyEqual; +import org.openrewrite.java.search.UsesType; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.JavaType; +import org.openrewrite.java.tree.TypeUtils; + +import java.util.ArrayList; +import java.util.List; + +@EqualsAndHashCode(callSuper = false) +@Value +public class RemoveDuplicateAnnotations extends Recipe { + + @Option(displayName = "Annotation type", + description = "The type of annotation to deduplicate.", + example = "org.jspecify.annotations.*") + String annotationType; + + String displayName = "Remove duplicate annotations"; + + String description = "Remove annotations that are repeated on the same element, keeping only the first occurrence. " + + "Duplicates typically arise when several distinct annotations are migrated to a single new annotation, " + + "such as when both `javax.annotation.Nullable` and `javax.annotation.CheckForNull` become " + + "`org.jspecify.annotations.Nullable`."; + + @Override + public TreeVisitor getVisitor() { + return Preconditions.check(new UsesType<>(annotationType, null), new JavaIsoVisitor() { + final TypeMatcher typeMatcher = new TypeMatcher(annotationType); + + @Override + public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) { + J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, ctx); + return cd.withLeadingAnnotations(removeDuplicates(cd.getLeadingAnnotations())); + } + + @Override + public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) { + J.MethodDeclaration md = super.visitMethodDeclaration(method, ctx); + return md.withLeadingAnnotations(removeDuplicates(md.getLeadingAnnotations())); + } + + @Override + public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations multiVariable, ExecutionContext ctx) { + J.VariableDeclarations mv = super.visitVariableDeclarations(multiVariable, ctx); + return mv.withLeadingAnnotations(removeDuplicates(mv.getLeadingAnnotations())); + } + + @Override + public J.Modifier visitModifier(J.Modifier modifier, ExecutionContext ctx) { + J.Modifier m = super.visitModifier(modifier, ctx); + return m.withAnnotations(removeDuplicates(m.getAnnotations())); + } + + @Override + public J.AnnotatedType visitAnnotatedType(J.AnnotatedType annotatedType, ExecutionContext ctx) { + J.AnnotatedType at = super.visitAnnotatedType(annotatedType, ctx); + return at.withAnnotations(removeDuplicates(at.getAnnotations())); + } + + @Override + public J.ArrayType visitArrayType(J.ArrayType arrayType, ExecutionContext ctx) { + J.ArrayType at = super.visitArrayType(arrayType, ctx); + return at.getAnnotations() == null ? at : at.withAnnotations(removeDuplicates(at.getAnnotations())); + } + + @Override + public J.Identifier visitIdentifier(J.Identifier identifier, ExecutionContext ctx) { + J.Identifier id = super.visitIdentifier(identifier, ctx); + return id.getAnnotations() == null ? id : id.withAnnotations(removeDuplicates(id.getAnnotations())); + } + + private List removeDuplicates(List annotations) { + if (annotations.size() < 2) { + return annotations; + } + List kept = new ArrayList<>(annotations.size()); + return ListUtils.map(annotations, annotation -> { + if (matchesType(annotation)) { + for (J.Annotation earlier : kept) { + if (SemanticallyEqual.areEqual(earlier, annotation)) { + return null; + } + } + } + kept.add(annotation); + return annotation; + }); + } + + private boolean matchesType(J.Annotation annotation) { + JavaType.FullyQualified fq = TypeUtils.asFullyQualified(annotation.getType()); + return fq != null && typeMatcher.matches(fq); + } + }); + } +} diff --git a/src/main/resources/META-INF/rewrite/jspecify.yml b/src/main/resources/META-INF/rewrite/jspecify.yml index 8a8d807ec3..42d4e1da69 100644 --- a/src/main/resources/META-INF/rewrite/jspecify.yml +++ b/src/main/resources/META-INF/rewrite/jspecify.yml @@ -85,6 +85,8 @@ recipeList: ignoreDefinition: true - org.openrewrite.staticanalysis.java.MoveFieldAnnotationToType: annotationType: org.jspecify.annotations.* + - org.openrewrite.java.migrate.jspecify.RemoveDuplicateAnnotations: + annotationType: org.jspecify.annotations.* --- type: specs.openrewrite.org/v1beta/recipe name: org.openrewrite.java.jspecify.MigrateFromJakartaAnnotationApi @@ -111,6 +113,8 @@ recipeList: ignoreDefinition: true - org.openrewrite.staticanalysis.java.MoveFieldAnnotationToType: annotationType: org.jspecify.annotations.* + - org.openrewrite.java.migrate.jspecify.RemoveDuplicateAnnotations: + annotationType: org.jspecify.annotations.* --- type: specs.openrewrite.org/v1beta/recipe name: org.openrewrite.java.jspecify.MigrateFromJetbrainsAnnotations @@ -137,6 +141,8 @@ recipeList: ignoreDefinition: true - org.openrewrite.staticanalysis.java.MoveFieldAnnotationToType: annotationType: org.jspecify.annotations.* + - org.openrewrite.java.migrate.jspecify.RemoveDuplicateAnnotations: + annotationType: org.jspecify.annotations.* --- type: specs.openrewrite.org/v1beta/recipe name: org.openrewrite.java.jspecify.MigrateFromMicrometerAnnotations @@ -163,6 +169,8 @@ recipeList: ignoreDefinition: true - org.openrewrite.staticanalysis.java.MoveFieldAnnotationToType: annotationType: org.jspecify.annotations.* + - org.openrewrite.java.migrate.jspecify.RemoveDuplicateAnnotations: + annotationType: org.jspecify.annotations.* --- type: specs.openrewrite.org/v1beta/recipe name: org.openrewrite.java.jspecify.MigrateFromSpringFrameworkAnnotations @@ -189,6 +197,8 @@ recipeList: ignoreDefinition: true - org.openrewrite.staticanalysis.java.MoveFieldAnnotationToType: annotationType: org.jspecify.annotations.* + - org.openrewrite.java.migrate.jspecify.RemoveDuplicateAnnotations: + annotationType: org.jspecify.annotations.* --- type: specs.openrewrite.org/v1beta/recipe name: org.openrewrite.java.jspecify.MigrateFromMicronautAnnotations @@ -215,3 +225,5 @@ recipeList: ignoreDefinition: true - org.openrewrite.staticanalysis.java.MoveFieldAnnotationToType: annotationType: org.jspecify.annotations.* + - org.openrewrite.java.migrate.jspecify.RemoveDuplicateAnnotations: + annotationType: org.jspecify.annotations.* diff --git a/src/main/resources/META-INF/rewrite/recipes.csv b/src/main/resources/META-INF/rewrite/recipes.csv index 12a82bd4a4..b8b7302945 100644 --- a/src/main/resources/META-INF/rewrite/recipes.csv +++ b/src/main/resources/META-INF/rewrite/recipes.csv @@ -1,13 +1,13 @@ ecosystem,packageName,name,displayName,description,recipeCount,category1,category2,category3,category4,category1Description,category2Description,category3Description,category4Description,options,dataTables maven,org.openrewrite.recipe:rewrite-migrate-java,com.google.guava.InlineGuavaMethods,Inline `guava` methods annotated with `@InlineMe`,Automatically generated recipes to inline method calls based on `@InlineMe` annotations discovered in the type table.,66,,,Guava,Google,,,,,, -maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.jspecify.JSpecifyBestPractices,JSpecify best practices,"Apply JSpecify best practices, such as migrating off of alternatives, and adding missing `@Nullable` annotations.",35,,,JSpecify,Java,,,Recipes for adopting [JSpecify](https://jspecify.dev/) nullability annotations.,Basic building blocks for transforming Java code.,, -maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.jspecify.MigrateFromJakartaAnnotationApi,Migrate from Jakarta annotation API to JSpecify,Migrate from Jakarta annotation API to JSpecify.,5,,,JSpecify,Java,,,Recipes for adopting [JSpecify](https://jspecify.dev/) nullability annotations.,Basic building blocks for transforming Java code.,, -maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.jspecify.MigrateFromJavaxAnnotationApi,Migrate from javax annotation API to JSpecify,Migrate from javax annotation API to JSpecify.,8,,,JSpecify,Java,,,Recipes for adopting [JSpecify](https://jspecify.dev/) nullability annotations.,Basic building blocks for transforming Java code.,, -maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.jspecify.MigrateFromJetbrainsAnnotations,Migrate from JetBrains annotations to JSpecify,Migrate from JetBrains annotations to JSpecify.,5,,,JSpecify,Java,,,Recipes for adopting [JSpecify](https://jspecify.dev/) nullability annotations.,Basic building blocks for transforming Java code.,, -maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.jspecify.MigrateFromMicrometerAnnotations,Migrate from Micrometer annotations to JSpecify,Migrate from Micrometer annotations to JSpecify.,6,,,JSpecify,Java,,,Recipes for adopting [JSpecify](https://jspecify.dev/) nullability annotations.,Basic building blocks for transforming Java code.,, -maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.jspecify.MigrateFromMicronautAnnotations,Migrate from Micronaut Framework annotations to JSpecify,Migrate from Micronaut Framework annotations to JSpecify.,5,,,JSpecify,Java,,,Recipes for adopting [JSpecify](https://jspecify.dev/) nullability annotations.,Basic building blocks for transforming Java code.,, -maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.jspecify.MigrateFromSpringFrameworkAnnotations,Migrate from Spring Framework annotations to JSpecify,Migrate from Spring Framework annotations to JSpecify.,6,,,JSpecify,Java,,,Recipes for adopting [JSpecify](https://jspecify.dev/) nullability annotations.,Basic building blocks for transforming Java code.,, -maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.jspecify.MigrateToJSpecify,Migrate to JSpecify,This recipe will migrate to JSpecify annotations from various other nullability annotation standards.,30,,,JSpecify,Java,,,Recipes for adopting [JSpecify](https://jspecify.dev/) nullability annotations.,Basic building blocks for transforming Java code.,, +maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.jspecify.JSpecifyBestPractices,JSpecify best practices,"Apply JSpecify best practices, such as migrating off of alternatives, and adding missing `@Nullable` annotations.",40,,,JSpecify,Java,,,Recipes for adopting [JSpecify](https://jspecify.dev/) nullability annotations.,Basic building blocks for transforming Java code.,, +maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.jspecify.MigrateFromJakartaAnnotationApi,Migrate from Jakarta annotation API to JSpecify,Migrate from Jakarta annotation API to JSpecify.,6,,,JSpecify,Java,,,Recipes for adopting [JSpecify](https://jspecify.dev/) nullability annotations.,Basic building blocks for transforming Java code.,, +maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.jspecify.MigrateFromJavaxAnnotationApi,Migrate from javax annotation API to JSpecify,Migrate from javax annotation API to JSpecify.,9,,,JSpecify,Java,,,Recipes for adopting [JSpecify](https://jspecify.dev/) nullability annotations.,Basic building blocks for transforming Java code.,, +maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.jspecify.MigrateFromJetbrainsAnnotations,Migrate from JetBrains annotations to JSpecify,Migrate from JetBrains annotations to JSpecify.,6,,,JSpecify,Java,,,Recipes for adopting [JSpecify](https://jspecify.dev/) nullability annotations.,Basic building blocks for transforming Java code.,, +maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.jspecify.MigrateFromMicrometerAnnotations,Migrate from Micrometer annotations to JSpecify,Migrate from Micrometer annotations to JSpecify.,7,,,JSpecify,Java,,,Recipes for adopting [JSpecify](https://jspecify.dev/) nullability annotations.,Basic building blocks for transforming Java code.,, +maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.jspecify.MigrateFromMicronautAnnotations,Migrate from Micronaut Framework annotations to JSpecify,Migrate from Micronaut Framework annotations to JSpecify.,6,,,JSpecify,Java,,,Recipes for adopting [JSpecify](https://jspecify.dev/) nullability annotations.,Basic building blocks for transforming Java code.,, +maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.jspecify.MigrateFromSpringFrameworkAnnotations,Migrate from Spring Framework annotations to JSpecify,Migrate from Spring Framework annotations to JSpecify.,7,,,JSpecify,Java,,,Recipes for adopting [JSpecify](https://jspecify.dev/) nullability annotations.,Basic building blocks for transforming Java code.,, +maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.jspecify.MigrateToJSpecify,Migrate to JSpecify,This recipe will migrate to JSpecify annotations from various other nullability annotation standards.,35,,,JSpecify,Java,,,Recipes for adopting [JSpecify](https://jspecify.dev/) nullability annotations.,Basic building blocks for transforming Java code.,, maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.AccessController,Remove Security AccessController,The Security Manager API is unsupported in Java 24. This recipe will remove the usage of `java.security.AccessController`.,4,,,Modernize,Java,,,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,, maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.AddJDeprScanPlugin,Add `JDeprScan` Maven Plug-in,Add the `JDeprScan` Maven plugin to scan class files for uses of deprecated APIs.,1,,,Modernize,Java,,,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,"[{""name"":""release"",""type"":""String"",""displayName"":""release"",""description"":""Specifies the Java SE release that provides the set of deprecated APIs for scanning."",""example"":""11""}]", maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.AddLombokMapstructBinding,Add `lombok-mapstruct-binding` when both MapStruct and Lombok are used,Add the `lombok-mapstruct-binding` annotation processor as needed when both MapStruct and Lombok are used.,6,,,Modernize,Java,,,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,,"[{""name"":""org.openrewrite.maven.table.MavenMetadataFailures"",""displayName"":""Maven metadata failures"",""instanceName"":""Maven metadata failures"",""description"":""Attempts to resolve maven metadata that failed."",""columns"":[{""name"":""group"",""type"":""String"",""displayName"":""Group id"",""description"":""The groupId of the artifact for which the metadata download failed.""},{""name"":""artifactId"",""type"":""String"",""displayName"":""Artifact id"",""description"":""The artifactId of the artifact for which the metadata download failed.""},{""name"":""version"",""type"":""String"",""displayName"":""Version"",""description"":""The version of the artifact for which the metadata download failed.""},{""name"":""mavenRepositoryUri"",""type"":""String"",""displayName"":""Maven repository"",""description"":""The URL of the Maven repository that the metadata download failed on.""},{""name"":""snapshots"",""type"":""String"",""displayName"":""Snapshots"",""description"":""Does the repository support snapshots.""},{""name"":""releases"",""type"":""String"",""displayName"":""Releases"",""description"":""Does the repository support releases.""},{""name"":""failure"",""type"":""String"",""displayName"":""Failure"",""description"":""The reason the metadata download failed.""}]}]" @@ -51,7 +51,7 @@ maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.J maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.JREThrowableFinalMethods,Rename final method declarations `getSuppressed()` and `addSuppressed(Throwable exception)` in classes that extend `Throwable`,The recipe renames `getSuppressed()` and `addSuppressed(Throwable exception)` methods in classes that extend `java.lang.Throwable` to `myGetSuppressed` and `myAddSuppressed(Throwable)`. These methods were added to Throwable in Java 7 and are marked final which cannot be overridden.,1,,,Modernize,Java,,,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,, maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.JREWrapperInterface,Add missing `isWrapperFor` and `unwrap` methods,Add method implementations stubs to classes that implement `java.sql.Wrapper`.,3,,,Modernize,Java,,,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,, maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.Java8toJava11,Migrate to Java 11,"This recipe will apply changes commonly needed when upgrading to Java 11. Specifically, for those applications that are built on Java 8, this recipe will update and add dependencies on J2EE libraries that are no longer directly bundled with the JDK. This recipe will also replace deprecated API with equivalents when there is a clear migration strategy. Build files will also be updated to use Java 11 as the target/source and plugins will be also be upgraded to versions that are compatible with Java 11.",324,,,Modernize,Java,,,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,,"[{""name"":""org.openrewrite.maven.table.MavenMetadataFailures"",""displayName"":""Maven metadata failures"",""instanceName"":""Maven metadata failures"",""description"":""Attempts to resolve maven metadata that failed."",""columns"":[{""name"":""group"",""type"":""String"",""displayName"":""Group id"",""description"":""The groupId of the artifact for which the metadata download failed.""},{""name"":""artifactId"",""type"":""String"",""displayName"":""Artifact id"",""description"":""The artifactId of the artifact for which the metadata download failed.""},{""name"":""version"",""type"":""String"",""displayName"":""Version"",""description"":""The version of the artifact for which the metadata download failed.""},{""name"":""mavenRepositoryUri"",""type"":""String"",""displayName"":""Maven repository"",""description"":""The URL of the Maven repository that the metadata download failed on.""},{""name"":""snapshots"",""type"":""String"",""displayName"":""Snapshots"",""description"":""Does the repository support snapshots.""},{""name"":""releases"",""type"":""String"",""displayName"":""Releases"",""description"":""Does the repository support releases.""},{""name"":""failure"",""type"":""String"",""displayName"":""Failure"",""description"":""The reason the metadata download failed.""}]}]" -maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.JavaBestPractices,Java best practices,"Applies opinionated best practices for Java projects targeting Java 25. This recipe includes the full Java 25 upgrade chain plus additional improvements to code style, API usage, and third-party dependency reduction that go beyond what the version migration recipes apply.",1755,,,Modernize,Java,,,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,,"[{""name"":""org.openrewrite.maven.table.MavenMetadataFailures"",""displayName"":""Maven metadata failures"",""instanceName"":""Maven metadata failures"",""description"":""Attempts to resolve maven metadata that failed."",""columns"":[{""name"":""group"",""type"":""String"",""displayName"":""Group id"",""description"":""The groupId of the artifact for which the metadata download failed.""},{""name"":""artifactId"",""type"":""String"",""displayName"":""Artifact id"",""description"":""The artifactId of the artifact for which the metadata download failed.""},{""name"":""version"",""type"":""String"",""displayName"":""Version"",""description"":""The version of the artifact for which the metadata download failed.""},{""name"":""mavenRepositoryUri"",""type"":""String"",""displayName"":""Maven repository"",""description"":""The URL of the Maven repository that the metadata download failed on.""},{""name"":""snapshots"",""type"":""String"",""displayName"":""Snapshots"",""description"":""Does the repository support snapshots.""},{""name"":""releases"",""type"":""String"",""displayName"":""Releases"",""description"":""Does the repository support releases.""},{""name"":""failure"",""type"":""String"",""displayName"":""Failure"",""description"":""The reason the metadata download failed.""}]}]" +maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.JavaBestPractices,Java best practices,"Applies opinionated best practices for Java projects targeting Java 25. This recipe includes the full Java 25 upgrade chain plus additional improvements to code style, API usage, and third-party dependency reduction that go beyond what the version migration recipes apply.",1760,,,Modernize,Java,,,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,,"[{""name"":""org.openrewrite.maven.table.MavenMetadataFailures"",""displayName"":""Maven metadata failures"",""instanceName"":""Maven metadata failures"",""description"":""Attempts to resolve maven metadata that failed."",""columns"":[{""name"":""group"",""type"":""String"",""displayName"":""Group id"",""description"":""The groupId of the artifact for which the metadata download failed.""},{""name"":""artifactId"",""type"":""String"",""displayName"":""Artifact id"",""description"":""The artifactId of the artifact for which the metadata download failed.""},{""name"":""version"",""type"":""String"",""displayName"":""Version"",""description"":""The version of the artifact for which the metadata download failed.""},{""name"":""mavenRepositoryUri"",""type"":""String"",""displayName"":""Maven repository"",""description"":""The URL of the Maven repository that the metadata download failed on.""},{""name"":""snapshots"",""type"":""String"",""displayName"":""Snapshots"",""description"":""Does the repository support snapshots.""},{""name"":""releases"",""type"":""String"",""displayName"":""Releases"",""description"":""Does the repository support releases.""},{""name"":""failure"",""type"":""String"",""displayName"":""Failure"",""description"":""The reason the metadata download failed.""}]}]" maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.JpaCacheProperties,Disable the persistence unit second-level cache,Sets an explicit value for the shared cache mode.,1,,,Modernize,Java,,,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,, maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.Jre17AgentMainPreMainPublic,Set visibility of `premain` and `agentmain` methods to `public`,Check for a behavior change in Java agents.,5,,,Modernize,Java,,,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,, maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.Krb5LoginModuleClass,Use `com.sun.security.auth.module.Krb5LoginModule` instead of `com.ibm.security.auth.module.Krb5LoginModule`,Do not use the `com.ibm.security.auth.module.Krb5LoginModule` class.,2,,,Modernize,Java,,,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,, @@ -214,8 +214,8 @@ maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.j maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.jakarta.FileuploadToFileUpload2,Migrate deprecated `org.apache.commons.fileload` packages to `org.apache.commons.fileload.core`,Migrate deprecated `org.apache.commons.fileload` packages to `org.apache.commons.fileload.core`.,6,,Jakarta,Modernize,Java,,Recipes for migrating to [Jakarta EE](https://jakarta.ee/).,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,, maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.jakarta.HasNoJakartaAnnotations,Project has no Jakarta annotations,Mark all source as found per `JavaProject` where no Jakarta annotations are found. This is useful mostly as a precondition for recipes that require Jakarta annotations to be present.,1,,Jakarta,Modernize,Java,,Recipes for migrating to [Jakarta EE](https://jakarta.ee/).,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,, maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.jakarta.JacksonJavaxToJakarta,Migrate Jackson from javax to jakarta namespace,Java EE has been rebranded to Jakarta EE. This recipe replaces existing Jackson dependencies with their counterparts that are compatible with Jakarta EE 9.,23,,Jakarta,Modernize,Java,,Recipes for migrating to [Jakarta EE](https://jakarta.ee/).,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,,"[{""name"":""org.openrewrite.maven.table.MavenMetadataFailures"",""displayName"":""Maven metadata failures"",""instanceName"":""Maven metadata failures"",""description"":""Attempts to resolve maven metadata that failed."",""columns"":[{""name"":""group"",""type"":""String"",""displayName"":""Group id"",""description"":""The groupId of the artifact for which the metadata download failed.""},{""name"":""artifactId"",""type"":""String"",""displayName"":""Artifact id"",""description"":""The artifactId of the artifact for which the metadata download failed.""},{""name"":""version"",""type"":""String"",""displayName"":""Version"",""description"":""The version of the artifact for which the metadata download failed.""},{""name"":""mavenRepositoryUri"",""type"":""String"",""displayName"":""Maven repository"",""description"":""The URL of the Maven repository that the metadata download failed on.""},{""name"":""snapshots"",""type"":""String"",""displayName"":""Snapshots"",""description"":""Does the repository support snapshots.""},{""name"":""releases"",""type"":""String"",""displayName"":""Releases"",""description"":""Does the repository support releases.""},{""name"":""failure"",""type"":""String"",""displayName"":""Failure"",""description"":""The reason the metadata download failed.""}]}]" -maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.jakarta.JakartaEE10,Migrate to Jakarta EE 10,"These recipes help with the Migration to Jakarta EE 10, flagging and updating deprecated methods.",596,,Jakarta,Modernize,Java,,Recipes for migrating to [Jakarta EE](https://jakarta.ee/).,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,,"[{""name"":""org.openrewrite.maven.table.MavenMetadataFailures"",""displayName"":""Maven metadata failures"",""instanceName"":""Maven metadata failures"",""description"":""Attempts to resolve maven metadata that failed."",""columns"":[{""name"":""group"",""type"":""String"",""displayName"":""Group id"",""description"":""The groupId of the artifact for which the metadata download failed.""},{""name"":""artifactId"",""type"":""String"",""displayName"":""Artifact id"",""description"":""The artifactId of the artifact for which the metadata download failed.""},{""name"":""version"",""type"":""String"",""displayName"":""Version"",""description"":""The version of the artifact for which the metadata download failed.""},{""name"":""mavenRepositoryUri"",""type"":""String"",""displayName"":""Maven repository"",""description"":""The URL of the Maven repository that the metadata download failed on.""},{""name"":""snapshots"",""type"":""String"",""displayName"":""Snapshots"",""description"":""Does the repository support snapshots.""},{""name"":""releases"",""type"":""String"",""displayName"":""Releases"",""description"":""Does the repository support releases.""},{""name"":""failure"",""type"":""String"",""displayName"":""Failure"",""description"":""The reason the metadata download failed.""}]}]" -maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.jakarta.JakartaEE11,Migrate to Jakarta EE 11,"These recipes help with the Migration to Jakarta EE 11, flagging and updating deprecated methods.",614,,Jakarta,Modernize,Java,,Recipes for migrating to [Jakarta EE](https://jakarta.ee/).,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,,"[{""name"":""org.openrewrite.maven.table.MavenMetadataFailures"",""displayName"":""Maven metadata failures"",""instanceName"":""Maven metadata failures"",""description"":""Attempts to resolve maven metadata that failed."",""columns"":[{""name"":""group"",""type"":""String"",""displayName"":""Group id"",""description"":""The groupId of the artifact for which the metadata download failed.""},{""name"":""artifactId"",""type"":""String"",""displayName"":""Artifact id"",""description"":""The artifactId of the artifact for which the metadata download failed.""},{""name"":""version"",""type"":""String"",""displayName"":""Version"",""description"":""The version of the artifact for which the metadata download failed.""},{""name"":""mavenRepositoryUri"",""type"":""String"",""displayName"":""Maven repository"",""description"":""The URL of the Maven repository that the metadata download failed on.""},{""name"":""snapshots"",""type"":""String"",""displayName"":""Snapshots"",""description"":""Does the repository support snapshots.""},{""name"":""releases"",""type"":""String"",""displayName"":""Releases"",""description"":""Does the repository support releases.""},{""name"":""failure"",""type"":""String"",""displayName"":""Failure"",""description"":""The reason the metadata download failed.""}]}]" +maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.jakarta.JakartaEE10,Migrate to Jakarta EE 10,"These recipes help with the Migration to Jakarta EE 10, flagging and updating deprecated methods.",597,,Jakarta,Modernize,Java,,Recipes for migrating to [Jakarta EE](https://jakarta.ee/).,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,,"[{""name"":""org.openrewrite.maven.table.MavenMetadataFailures"",""displayName"":""Maven metadata failures"",""instanceName"":""Maven metadata failures"",""description"":""Attempts to resolve maven metadata that failed."",""columns"":[{""name"":""group"",""type"":""String"",""displayName"":""Group id"",""description"":""The groupId of the artifact for which the metadata download failed.""},{""name"":""artifactId"",""type"":""String"",""displayName"":""Artifact id"",""description"":""The artifactId of the artifact for which the metadata download failed.""},{""name"":""version"",""type"":""String"",""displayName"":""Version"",""description"":""The version of the artifact for which the metadata download failed.""},{""name"":""mavenRepositoryUri"",""type"":""String"",""displayName"":""Maven repository"",""description"":""The URL of the Maven repository that the metadata download failed on.""},{""name"":""snapshots"",""type"":""String"",""displayName"":""Snapshots"",""description"":""Does the repository support snapshots.""},{""name"":""releases"",""type"":""String"",""displayName"":""Releases"",""description"":""Does the repository support releases.""},{""name"":""failure"",""type"":""String"",""displayName"":""Failure"",""description"":""The reason the metadata download failed.""}]}]" +maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.jakarta.JakartaEE11,Migrate to Jakarta EE 11,"These recipes help with the Migration to Jakarta EE 11, flagging and updating deprecated methods.",615,,Jakarta,Modernize,Java,,Recipes for migrating to [Jakarta EE](https://jakarta.ee/).,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,,"[{""name"":""org.openrewrite.maven.table.MavenMetadataFailures"",""displayName"":""Maven metadata failures"",""instanceName"":""Maven metadata failures"",""description"":""Attempts to resolve maven metadata that failed."",""columns"":[{""name"":""group"",""type"":""String"",""displayName"":""Group id"",""description"":""The groupId of the artifact for which the metadata download failed.""},{""name"":""artifactId"",""type"":""String"",""displayName"":""Artifact id"",""description"":""The artifactId of the artifact for which the metadata download failed.""},{""name"":""version"",""type"":""String"",""displayName"":""Version"",""description"":""The version of the artifact for which the metadata download failed.""},{""name"":""mavenRepositoryUri"",""type"":""String"",""displayName"":""Maven repository"",""description"":""The URL of the Maven repository that the metadata download failed on.""},{""name"":""snapshots"",""type"":""String"",""displayName"":""Snapshots"",""description"":""Does the repository support snapshots.""},{""name"":""releases"",""type"":""String"",""displayName"":""Releases"",""description"":""Does the repository support releases.""},{""name"":""failure"",""type"":""String"",""displayName"":""Failure"",""description"":""The reason the metadata download failed.""}]}]" maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.jakarta.JakartaFacesConfigXml4,Migrate xmlns entries in `faces-config.xml` files,Jakarta EE 10 uses Faces version 4.,3,,Jakarta,Modernize,Java,,Recipes for migrating to [Jakarta EE](https://jakarta.ee/).,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,, maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.jakarta.JakartaFacesEcmaScript,Migrate JSF values inside EcmaScript files,"Convert JSF to Faces values inside JavaScript,TypeScript, and Properties files.",4,,Jakarta,Modernize,Java,,Recipes for migrating to [Jakarta EE](https://jakarta.ee/).,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,, maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.jakarta.JakartaFacesTagLibraryXml4,Migrate xmlns entries in `taglib.xml` files,Faces 4 uses facelet-taglib 4.0.,3,,Jakarta,Modernize,Java,,Recipes for migrating to [Jakarta EE](https://jakarta.ee/).,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,, @@ -245,7 +245,7 @@ maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.j maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.jakarta.JavaxJspToJakartaJsp,Migrate deprecated `javax.jsp` packages to `jakarta.jsp`,"Java EE has been rebranded to Jakarta EE, necessitating a package relocation.",6,,Jakarta,Modernize,Java,,Recipes for migrating to [Jakarta EE](https://jakarta.ee/).,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,, maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.jakarta.JavaxJwsToJakartaJws,Migrate deprecated `javax.jws` packages to `jakarta.jws`,"Java EE has been rebranded to Jakarta EE, necessitating a package relocation.",6,,Jakarta,Modernize,Java,,Recipes for migrating to [Jakarta EE](https://jakarta.ee/).,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,, maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.jakarta.JavaxMailToJakartaMail,Migrate deprecated `javax.mail` packages to `jakarta.mail`,"Java EE has been rebranded to Jakarta EE, necessitating a package relocation.",8,,Jakarta,Modernize,Java,,Recipes for migrating to [Jakarta EE](https://jakarta.ee/).,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,, -maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.jakarta.JavaxMigrationToJakarta,Migrate to Jakarta EE 9,Jakarta EE 9 is the first version of Jakarta EE that uses the new `jakarta` namespace.,370,,Jakarta,Modernize,Java,,Recipes for migrating to [Jakarta EE](https://jakarta.ee/).,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,,"[{""name"":""org.openrewrite.maven.table.MavenMetadataFailures"",""displayName"":""Maven metadata failures"",""instanceName"":""Maven metadata failures"",""description"":""Attempts to resolve maven metadata that failed."",""columns"":[{""name"":""group"",""type"":""String"",""displayName"":""Group id"",""description"":""The groupId of the artifact for which the metadata download failed.""},{""name"":""artifactId"",""type"":""String"",""displayName"":""Artifact id"",""description"":""The artifactId of the artifact for which the metadata download failed.""},{""name"":""version"",""type"":""String"",""displayName"":""Version"",""description"":""The version of the artifact for which the metadata download failed.""},{""name"":""mavenRepositoryUri"",""type"":""String"",""displayName"":""Maven repository"",""description"":""The URL of the Maven repository that the metadata download failed on.""},{""name"":""snapshots"",""type"":""String"",""displayName"":""Snapshots"",""description"":""Does the repository support snapshots.""},{""name"":""releases"",""type"":""String"",""displayName"":""Releases"",""description"":""Does the repository support releases.""},{""name"":""failure"",""type"":""String"",""displayName"":""Failure"",""description"":""The reason the metadata download failed.""}]}]" +maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.jakarta.JavaxMigrationToJakarta,Migrate to Jakarta EE 9,Jakarta EE 9 is the first version of Jakarta EE that uses the new `jakarta` namespace.,371,,Jakarta,Modernize,Java,,Recipes for migrating to [Jakarta EE](https://jakarta.ee/).,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,,"[{""name"":""org.openrewrite.maven.table.MavenMetadataFailures"",""displayName"":""Maven metadata failures"",""instanceName"":""Maven metadata failures"",""description"":""Attempts to resolve maven metadata that failed."",""columns"":[{""name"":""group"",""type"":""String"",""displayName"":""Group id"",""description"":""The groupId of the artifact for which the metadata download failed.""},{""name"":""artifactId"",""type"":""String"",""displayName"":""Artifact id"",""description"":""The artifactId of the artifact for which the metadata download failed.""},{""name"":""version"",""type"":""String"",""displayName"":""Version"",""description"":""The version of the artifact for which the metadata download failed.""},{""name"":""mavenRepositoryUri"",""type"":""String"",""displayName"":""Maven repository"",""description"":""The URL of the Maven repository that the metadata download failed on.""},{""name"":""snapshots"",""type"":""String"",""displayName"":""Snapshots"",""description"":""Does the repository support snapshots.""},{""name"":""releases"",""type"":""String"",""displayName"":""Releases"",""description"":""Does the repository support releases.""},{""name"":""failure"",""type"":""String"",""displayName"":""Failure"",""description"":""The reason the metadata download failed.""}]}]" maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.jakarta.JavaxOrmXmlToJakartaOrmXml,Migrate xmlns entries in `orm.xml` files,"Java EE has been rebranded to Jakarta EE, necessitating an XML namespace relocation.",4,,Jakarta,Modernize,Java,,Recipes for migrating to [Jakarta EE](https://jakarta.ee/).,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,, maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.jakarta.JavaxPersistenceToJakartaPersistence,Migrate deprecated `javax.persistence` packages to `jakarta.persistence`,"Java EE has been rebranded to Jakarta EE, necessitating a package relocation.",6,,Jakarta,Modernize,Java,,Recipes for migrating to [Jakarta EE](https://jakarta.ee/).,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,, maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.jakarta.JavaxPersistenceXmlToJakartaPersistenceXml,Migrate xmlns entries in `persistence.xml` files,"Java EE has been rebranded to Jakarta EE, necessitating an XML namespace relocation.",6,,Jakarta,Modernize,Java,,Recipes for migrating to [Jakarta EE](https://jakarta.ee/).,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,, @@ -254,7 +254,7 @@ maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.j maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.jakarta.JavaxServletToJakartaServlet,Migrate deprecated `javax.servlet` packages to `jakarta.servlet`,"Java EE has been rebranded to Jakarta EE, necessitating a package relocation.",5,,Jakarta,Modernize,Java,,Recipes for migrating to [Jakarta EE](https://jakarta.ee/).,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,, maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.jakarta.JavaxToJakartaCdiExtensions,Rename CDI Extension to Jakarta,Rename `javax.enterprise.inject.spi.Extension` to `jakarta.enterprise.inject.spi.Extension`.,2,,Jakarta,Modernize,Java,,Recipes for migrating to [Jakarta EE](https://jakarta.ee/).,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,, maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.jakarta.JavaxTransactionMigrationToJakartaTransaction,Migrate deprecated `javax.transaction` packages to `jakarta.transaction`,"Java EE has been rebranded to Jakarta EE, necessitating a package relocation.",6,,Jakarta,Modernize,Java,,Recipes for migrating to [Jakarta EE](https://jakarta.ee/).,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,, -maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.jakarta.JavaxValidationMigrationToJakartaValidation,Migrate deprecated `javax.validation` packages to `jakarta.validation`,"Java EE has been rebranded to Jakarta EE, necessitating a package relocation.",8,,Jakarta,Modernize,Java,,Recipes for migrating to [Jakarta EE](https://jakarta.ee/).,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,, +maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.jakarta.JavaxValidationMigrationToJakartaValidation,Migrate deprecated `javax.validation` packages to `jakarta.validation`,"Java EE has been rebranded to Jakarta EE, necessitating a package relocation.",9,,Jakarta,Modernize,Java,,Recipes for migrating to [Jakarta EE](https://jakarta.ee/).,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,, maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.jakarta.JavaxWebFragmentXmlToJakartaWebFragmentXml,Migrate xmlns entries in `web-fragment.xml` files,"Java EE has been rebranded to Jakarta EE, necessitating an XML namespace relocation.",6,,Jakarta,Modernize,Java,,Recipes for migrating to [Jakarta EE](https://jakarta.ee/).,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,, maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.jakarta.JavaxWebXmlToJakartaWebXml,Migrate xmlns entries in `web.xml` files,"Java EE has been rebranded to Jakarta EE, necessitating an XML namespace relocation.",6,,Jakarta,Modernize,Java,,Recipes for migrating to [Jakarta EE](https://jakarta.ee/).,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,, maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.jakarta.JavaxWebsocketToJakartaWebsocket,Migrate deprecated `javax.websocket` packages to `jakarta.websocket`,"Java EE has been rebranded to Jakarta EE, necessitating a package relocation.",9,,Jakarta,Modernize,Java,,Recipes for migrating to [Jakarta EE](https://jakarta.ee/).,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,, @@ -360,6 +360,7 @@ maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.j maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.javax.UseJoinColumnForMapping,`@JoinColumn` annotations must be used with relationship mappings,"In OpenJPA, when a relationship attribute has either a `@OneToOne` or a `@ManyToOne` annotation with a `@Column` annotation, the `@Column` annotation is treated as a `@JoinColumn` annotation. EclipseLink throws an exception that indicates that the entity class must use `@JoinColumn` instead of `@Column` to map a relationship attribute.",1,,`javax` APIs,Modernize,Java,,,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,, maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.javax.openJPAToEclipseLink,Migrate from OpenJPA to EclipseLink JPA,These recipes help migrate Java Persistence applications using OpenJPA to EclipseLink JPA.,10,,`javax` APIs,Modernize,Java,,,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,, maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.jspecify.MoveAnnotationToArrayType,Move annotation to array type,"When an annotation like `@Nullable` is applied to an array type in declaration position, this recipe moves it to the array brackets. For example, `@Nullable byte[]` becomes `byte @Nullable[]`. Best used before `ChangeType` in a migration pipeline, targeting the pre-migration annotation type.",1,,Jspecify,Modernize,Java,,,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,"[{""name"":""annotationType"",""type"":""String"",""displayName"":""Annotation type"",""description"":""The type of annotation to move to the array type. Should target the pre-migration annotation type to avoid changing the semantics of pre-existing type-use annotations on object arrays."",""example"":""javax.annotation.*ull*"",""required"":true}]", +maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.jspecify.RemoveDuplicateAnnotations,Remove duplicate annotations,"Remove annotations that are repeated on the same element, keeping only the first occurrence. Duplicates typically arise when several distinct annotations are migrated to a single new annotation, such as when both `javax.annotation.Nullable` and `javax.annotation.CheckForNull` become `org.jspecify.annotations.Nullable`.",1,,Jspecify,Modernize,Java,,,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,"[{""name"":""annotationType"",""type"":""String"",""displayName"":""Annotation type"",""description"":""The type of annotation to deduplicate."",""example"":""org.jspecify.annotations.*"",""required"":true}]", maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.lang.ExplicitRecordImport,Add explicit import for `Record` classes,"Add explicit import for `Record` classes when upgrading past Java 14+, to avoid conflicts with `java.lang.Record`.",1,,`java.lang` APIs,Modernize,Java,,,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,, maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.lang.ExtractExplicitConstructorInvocationArguments,Extract complex `super(..)` and `this(..)` arguments into local variables,"[JEP 513](https://openjdk.org/jeps/513) allows statements before an explicit `super(..)` or `this(..)` constructor invocation. When such a call computes one of its arguments through a method invocation or object creation, this recipe extracts the non-trivial arguments into local variables declared right before the call, surfacing the work done before construction. diff --git a/src/test/java/org/openrewrite/java/migrate/jspecify/JSpecifyBestPracticesTest.java b/src/test/java/org/openrewrite/java/migrate/jspecify/JSpecifyBestPracticesTest.java index 207f2bdd7d..0cb3730e27 100644 --- a/src/test/java/org/openrewrite/java/migrate/jspecify/JSpecifyBestPracticesTest.java +++ b/src/test/java/org/openrewrite/java/migrate/jspecify/JSpecifyBestPracticesTest.java @@ -226,6 +226,40 @@ public void baz(@Nullable String[] a) { ); } + @Issue("https://github.com/openrewrite/rewrite-migrate-java/issues/1199") + @Test + void doNotDuplicateNullableWhenSeveralAnnotationsMapToJspecify() { + rewriteRun( + //language=java + java( + """ + import javax.annotation.CheckForNull; + import javax.annotation.Nullable; + + class Foo { + @Nullable + @CheckForNull + private String field; + + public void bar(@Nullable @org.jetbrains.annotations.Nullable String baz) { + } + } + """, + """ + import org.jspecify.annotations.Nullable; + + class Foo { + @Nullable + private String field; + + public void bar(@Nullable String baz) { + } + } + """ + ) + ); + } + @Test void migrateFromJakartaAnnotationApiToJspecify() { rewriteRun( diff --git a/src/test/java/org/openrewrite/java/migrate/jspecify/RemoveDuplicateAnnotationsTest.java b/src/test/java/org/openrewrite/java/migrate/jspecify/RemoveDuplicateAnnotationsTest.java new file mode 100644 index 0000000000..03f8a98b0c --- /dev/null +++ b/src/test/java/org/openrewrite/java/migrate/jspecify/RemoveDuplicateAnnotationsTest.java @@ -0,0 +1,220 @@ +/* + * Copyright 2026 the original author or authors. + *

+ * Licensed under the Moderne Source Available License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * https://docs.moderne.io/licensing/moderne-source-available-license + *

+ * 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.migrate.jspecify; + +import org.junit.jupiter.api.Test; +import org.openrewrite.DocumentExample; +import org.openrewrite.Issue; +import org.openrewrite.java.JavaParser; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +import static org.openrewrite.java.Assertions.java; + +@Issue("https://github.com/openrewrite/rewrite-migrate-java/issues/1199") +class RemoveDuplicateAnnotationsTest implements RewriteTest { + + @Override + public void defaults(RecipeSpec spec) { + spec + .recipe(new RemoveDuplicateAnnotations("org.jspecify.annotations.*")) + .parser(JavaParser.fromJavaVersion().classpath("jspecify")); + } + + @DocumentExample + @Test + void removeDuplicateAnnotationOnMethodParameter() { + rewriteRun( + //language=java + java( + """ + import org.jspecify.annotations.Nullable; + + class Foo { + public void add(@Nullable @org.jspecify.annotations.Nullable final String bar) { + } + } + """, + """ + import org.jspecify.annotations.Nullable; + + class Foo { + public void add(@Nullable final String bar) { + } + } + """ + ) + ); + } + + @Test + void removeDuplicateAnnotationOnField() { + rewriteRun( + //language=java + java( + """ + import org.jspecify.annotations.Nullable; + + class Foo { + @Nullable + @Nullable + private String bar; + } + """, + """ + import org.jspecify.annotations.Nullable; + + class Foo { + @Nullable + private String bar; + } + """ + ) + ); + } + + @Test + void removeDuplicateAnnotationOnMethodReturnType() { + rewriteRun( + //language=java + java( + """ + import org.jspecify.annotations.Nullable; + + class Foo { + @Nullable + @Nullable + public String bar() { + return null; + } + } + """, + """ + import org.jspecify.annotations.Nullable; + + class Foo { + @Nullable + public String bar() { + return null; + } + } + """ + ) + ); + } + + @Test + void removeDuplicateAnnotationOnClass() { + rewriteRun( + //language=java + java( + """ + import org.jspecify.annotations.NullMarked; + + @NullMarked + @NullMarked + class Foo { + } + """, + """ + import org.jspecify.annotations.NullMarked; + + @NullMarked + class Foo { + } + """ + ) + ); + } + + @Test + void removeDuplicateTypeUseAnnotation() { + rewriteRun( + //language=java + java( + """ + import java.util.List; + import org.jspecify.annotations.Nullable; + + class Foo { + private List<@Nullable @Nullable String> bar; + } + """, + """ + import java.util.List; + import org.jspecify.annotations.Nullable; + + class Foo { + private List<@Nullable String> bar; + } + """ + ) + ); + } + + @Test + void retainSingleAnnotation() { + rewriteRun( + //language=java + java( + """ + import org.jspecify.annotations.NonNull; + import org.jspecify.annotations.Nullable; + + class Foo { + public @Nullable String bar(@NonNull String baz) { + return null; + } + } + """ + ) + ); + } + + @Test + void retainDistinctAnnotations() { + rewriteRun( + //language=java + java( + """ + import org.jspecify.annotations.NonNull; + import org.jspecify.annotations.Nullable; + + class Foo { + public void bar(@Nullable @NonNull String baz) { + } + } + """ + ) + ); + } + + @Test + void retainAnnotationsOfOtherTypes() { + rewriteRun( + //language=java + java( + """ + class Foo { + @SuppressWarnings("all") + public void bar() { + } + } + """ + ) + ); + } +} From a85b0b61d641b635b831c3bc90440ef9cbbb21b7 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Thu, 13 Aug 2026 19:48:21 +0200 Subject: [PATCH 2/3] Use ListUtils.filter to drop duplicate annotations --- .../jspecify/RemoveDuplicateAnnotations.java | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/openrewrite/java/migrate/jspecify/RemoveDuplicateAnnotations.java b/src/main/java/org/openrewrite/java/migrate/jspecify/RemoveDuplicateAnnotations.java index ed9a6232a4..e1ed899fdd 100644 --- a/src/main/java/org/openrewrite/java/migrate/jspecify/RemoveDuplicateAnnotations.java +++ b/src/main/java/org/openrewrite/java/migrate/jspecify/RemoveDuplicateAnnotations.java @@ -17,6 +17,7 @@ import lombok.EqualsAndHashCode; import lombok.Value; +import org.jspecify.annotations.Nullable; import org.openrewrite.*; import org.openrewrite.internal.ListUtils; import org.openrewrite.java.JavaIsoVisitor; @@ -84,30 +85,27 @@ public J.AnnotatedType visitAnnotatedType(J.AnnotatedType annotatedType, Executi @Override public J.ArrayType visitArrayType(J.ArrayType arrayType, ExecutionContext ctx) { J.ArrayType at = super.visitArrayType(arrayType, ctx); - return at.getAnnotations() == null ? at : at.withAnnotations(removeDuplicates(at.getAnnotations())); + return at.withAnnotations(removeDuplicates(at.getAnnotations())); } @Override public J.Identifier visitIdentifier(J.Identifier identifier, ExecutionContext ctx) { J.Identifier id = super.visitIdentifier(identifier, ctx); - return id.getAnnotations() == null ? id : id.withAnnotations(removeDuplicates(id.getAnnotations())); + return id.withAnnotations(removeDuplicates(id.getAnnotations())); } - private List removeDuplicates(List annotations) { - if (annotations.size() < 2) { + private @Nullable List removeDuplicates(@Nullable List annotations) { + if (annotations == null || annotations.size() < 2) { return annotations; } List kept = new ArrayList<>(annotations.size()); - return ListUtils.map(annotations, annotation -> { - if (matchesType(annotation)) { - for (J.Annotation earlier : kept) { - if (SemanticallyEqual.areEqual(earlier, annotation)) { - return null; - } - } + return ListUtils.filter(annotations, annotation -> { + if (matchesType(annotation) && + kept.stream().anyMatch(earlier -> SemanticallyEqual.areEqual(earlier, annotation))) { + return false; } kept.add(annotation); - return annotation; + return true; }); } From 652f6b15d25126ad8734362d1a0f8383cfa5129c Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Thu, 13 Aug 2026 20:16:24 +0200 Subject: [PATCH 3/3] Move `RemoveDuplicateAnnotations` to rewrite-static-analysis The recipe is not specific to JSpecify: any many-to-one `ChangeType` mapping can leave an element annotated twice. It now lives in rewrite-static-analysis, next to `RemoveDuplicateConditions`, with an optional `annotationType` defaulting to any annotation and a guard for `@Repeatable` annotations. See https://github.com/openrewrite/rewrite-static-analysis/pull/1000 --- .../jspecify/RemoveDuplicateAnnotations.java | 118 ---------- .../resources/META-INF/rewrite/jspecify.yml | 12 +- .../resources/META-INF/rewrite/recipes.csv | 1 - .../RemoveDuplicateAnnotationsTest.java | 220 ------------------ 4 files changed, 6 insertions(+), 345 deletions(-) delete mode 100644 src/main/java/org/openrewrite/java/migrate/jspecify/RemoveDuplicateAnnotations.java delete mode 100644 src/test/java/org/openrewrite/java/migrate/jspecify/RemoveDuplicateAnnotationsTest.java diff --git a/src/main/java/org/openrewrite/java/migrate/jspecify/RemoveDuplicateAnnotations.java b/src/main/java/org/openrewrite/java/migrate/jspecify/RemoveDuplicateAnnotations.java deleted file mode 100644 index e1ed899fdd..0000000000 --- a/src/main/java/org/openrewrite/java/migrate/jspecify/RemoveDuplicateAnnotations.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2026 the original author or authors. - *

- * Licensed under the Moderne Source Available License (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * https://docs.moderne.io/licensing/moderne-source-available-license - *

- * 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.migrate.jspecify; - -import lombok.EqualsAndHashCode; -import lombok.Value; -import org.jspecify.annotations.Nullable; -import org.openrewrite.*; -import org.openrewrite.internal.ListUtils; -import org.openrewrite.java.JavaIsoVisitor; -import org.openrewrite.java.TypeMatcher; -import org.openrewrite.java.search.SemanticallyEqual; -import org.openrewrite.java.search.UsesType; -import org.openrewrite.java.tree.J; -import org.openrewrite.java.tree.JavaType; -import org.openrewrite.java.tree.TypeUtils; - -import java.util.ArrayList; -import java.util.List; - -@EqualsAndHashCode(callSuper = false) -@Value -public class RemoveDuplicateAnnotations extends Recipe { - - @Option(displayName = "Annotation type", - description = "The type of annotation to deduplicate.", - example = "org.jspecify.annotations.*") - String annotationType; - - String displayName = "Remove duplicate annotations"; - - String description = "Remove annotations that are repeated on the same element, keeping only the first occurrence. " + - "Duplicates typically arise when several distinct annotations are migrated to a single new annotation, " + - "such as when both `javax.annotation.Nullable` and `javax.annotation.CheckForNull` become " + - "`org.jspecify.annotations.Nullable`."; - - @Override - public TreeVisitor getVisitor() { - return Preconditions.check(new UsesType<>(annotationType, null), new JavaIsoVisitor() { - final TypeMatcher typeMatcher = new TypeMatcher(annotationType); - - @Override - public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) { - J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, ctx); - return cd.withLeadingAnnotations(removeDuplicates(cd.getLeadingAnnotations())); - } - - @Override - public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) { - J.MethodDeclaration md = super.visitMethodDeclaration(method, ctx); - return md.withLeadingAnnotations(removeDuplicates(md.getLeadingAnnotations())); - } - - @Override - public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations multiVariable, ExecutionContext ctx) { - J.VariableDeclarations mv = super.visitVariableDeclarations(multiVariable, ctx); - return mv.withLeadingAnnotations(removeDuplicates(mv.getLeadingAnnotations())); - } - - @Override - public J.Modifier visitModifier(J.Modifier modifier, ExecutionContext ctx) { - J.Modifier m = super.visitModifier(modifier, ctx); - return m.withAnnotations(removeDuplicates(m.getAnnotations())); - } - - @Override - public J.AnnotatedType visitAnnotatedType(J.AnnotatedType annotatedType, ExecutionContext ctx) { - J.AnnotatedType at = super.visitAnnotatedType(annotatedType, ctx); - return at.withAnnotations(removeDuplicates(at.getAnnotations())); - } - - @Override - public J.ArrayType visitArrayType(J.ArrayType arrayType, ExecutionContext ctx) { - J.ArrayType at = super.visitArrayType(arrayType, ctx); - return at.withAnnotations(removeDuplicates(at.getAnnotations())); - } - - @Override - public J.Identifier visitIdentifier(J.Identifier identifier, ExecutionContext ctx) { - J.Identifier id = super.visitIdentifier(identifier, ctx); - return id.withAnnotations(removeDuplicates(id.getAnnotations())); - } - - private @Nullable List removeDuplicates(@Nullable List annotations) { - if (annotations == null || annotations.size() < 2) { - return annotations; - } - List kept = new ArrayList<>(annotations.size()); - return ListUtils.filter(annotations, annotation -> { - if (matchesType(annotation) && - kept.stream().anyMatch(earlier -> SemanticallyEqual.areEqual(earlier, annotation))) { - return false; - } - kept.add(annotation); - return true; - }); - } - - private boolean matchesType(J.Annotation annotation) { - JavaType.FullyQualified fq = TypeUtils.asFullyQualified(annotation.getType()); - return fq != null && typeMatcher.matches(fq); - } - }); - } -} diff --git a/src/main/resources/META-INF/rewrite/jspecify.yml b/src/main/resources/META-INF/rewrite/jspecify.yml index 42d4e1da69..001d363b92 100644 --- a/src/main/resources/META-INF/rewrite/jspecify.yml +++ b/src/main/resources/META-INF/rewrite/jspecify.yml @@ -85,7 +85,7 @@ recipeList: ignoreDefinition: true - org.openrewrite.staticanalysis.java.MoveFieldAnnotationToType: annotationType: org.jspecify.annotations.* - - org.openrewrite.java.migrate.jspecify.RemoveDuplicateAnnotations: + - org.openrewrite.staticanalysis.RemoveDuplicateAnnotations: annotationType: org.jspecify.annotations.* --- type: specs.openrewrite.org/v1beta/recipe @@ -113,7 +113,7 @@ recipeList: ignoreDefinition: true - org.openrewrite.staticanalysis.java.MoveFieldAnnotationToType: annotationType: org.jspecify.annotations.* - - org.openrewrite.java.migrate.jspecify.RemoveDuplicateAnnotations: + - org.openrewrite.staticanalysis.RemoveDuplicateAnnotations: annotationType: org.jspecify.annotations.* --- type: specs.openrewrite.org/v1beta/recipe @@ -141,7 +141,7 @@ recipeList: ignoreDefinition: true - org.openrewrite.staticanalysis.java.MoveFieldAnnotationToType: annotationType: org.jspecify.annotations.* - - org.openrewrite.java.migrate.jspecify.RemoveDuplicateAnnotations: + - org.openrewrite.staticanalysis.RemoveDuplicateAnnotations: annotationType: org.jspecify.annotations.* --- type: specs.openrewrite.org/v1beta/recipe @@ -169,7 +169,7 @@ recipeList: ignoreDefinition: true - org.openrewrite.staticanalysis.java.MoveFieldAnnotationToType: annotationType: org.jspecify.annotations.* - - org.openrewrite.java.migrate.jspecify.RemoveDuplicateAnnotations: + - org.openrewrite.staticanalysis.RemoveDuplicateAnnotations: annotationType: org.jspecify.annotations.* --- type: specs.openrewrite.org/v1beta/recipe @@ -197,7 +197,7 @@ recipeList: ignoreDefinition: true - org.openrewrite.staticanalysis.java.MoveFieldAnnotationToType: annotationType: org.jspecify.annotations.* - - org.openrewrite.java.migrate.jspecify.RemoveDuplicateAnnotations: + - org.openrewrite.staticanalysis.RemoveDuplicateAnnotations: annotationType: org.jspecify.annotations.* --- type: specs.openrewrite.org/v1beta/recipe @@ -225,5 +225,5 @@ recipeList: ignoreDefinition: true - org.openrewrite.staticanalysis.java.MoveFieldAnnotationToType: annotationType: org.jspecify.annotations.* - - org.openrewrite.java.migrate.jspecify.RemoveDuplicateAnnotations: + - org.openrewrite.staticanalysis.RemoveDuplicateAnnotations: annotationType: org.jspecify.annotations.* diff --git a/src/main/resources/META-INF/rewrite/recipes.csv b/src/main/resources/META-INF/rewrite/recipes.csv index b8b7302945..dbb7a45637 100644 --- a/src/main/resources/META-INF/rewrite/recipes.csv +++ b/src/main/resources/META-INF/rewrite/recipes.csv @@ -360,7 +360,6 @@ maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.j maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.javax.UseJoinColumnForMapping,`@JoinColumn` annotations must be used with relationship mappings,"In OpenJPA, when a relationship attribute has either a `@OneToOne` or a `@ManyToOne` annotation with a `@Column` annotation, the `@Column` annotation is treated as a `@JoinColumn` annotation. EclipseLink throws an exception that indicates that the entity class must use `@JoinColumn` instead of `@Column` to map a relationship attribute.",1,,`javax` APIs,Modernize,Java,,,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,, maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.javax.openJPAToEclipseLink,Migrate from OpenJPA to EclipseLink JPA,These recipes help migrate Java Persistence applications using OpenJPA to EclipseLink JPA.,10,,`javax` APIs,Modernize,Java,,,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,, maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.jspecify.MoveAnnotationToArrayType,Move annotation to array type,"When an annotation like `@Nullable` is applied to an array type in declaration position, this recipe moves it to the array brackets. For example, `@Nullable byte[]` becomes `byte @Nullable[]`. Best used before `ChangeType` in a migration pipeline, targeting the pre-migration annotation type.",1,,Jspecify,Modernize,Java,,,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,"[{""name"":""annotationType"",""type"":""String"",""displayName"":""Annotation type"",""description"":""The type of annotation to move to the array type. Should target the pre-migration annotation type to avoid changing the semantics of pre-existing type-use annotations on object arrays."",""example"":""javax.annotation.*ull*"",""required"":true}]", -maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.jspecify.RemoveDuplicateAnnotations,Remove duplicate annotations,"Remove annotations that are repeated on the same element, keeping only the first occurrence. Duplicates typically arise when several distinct annotations are migrated to a single new annotation, such as when both `javax.annotation.Nullable` and `javax.annotation.CheckForNull` become `org.jspecify.annotations.Nullable`.",1,,Jspecify,Modernize,Java,,,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,"[{""name"":""annotationType"",""type"":""String"",""displayName"":""Annotation type"",""description"":""The type of annotation to deduplicate."",""example"":""org.jspecify.annotations.*"",""required"":true}]", maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.lang.ExplicitRecordImport,Add explicit import for `Record` classes,"Add explicit import for `Record` classes when upgrading past Java 14+, to avoid conflicts with `java.lang.Record`.",1,,`java.lang` APIs,Modernize,Java,,,Modernize your code to best use the project's current JDK version. Take advantage of newly available APIs and reduce the dependency of your code on third party dependencies where there is equivalent functionality in the Java standard library.,Basic building blocks for transforming Java code.,, maven,org.openrewrite.recipe:rewrite-migrate-java,org.openrewrite.java.migrate.lang.ExtractExplicitConstructorInvocationArguments,Extract complex `super(..)` and `this(..)` arguments into local variables,"[JEP 513](https://openjdk.org/jeps/513) allows statements before an explicit `super(..)` or `this(..)` constructor invocation. When such a call computes one of its arguments through a method invocation or object creation, this recipe extracts the non-trivial arguments into local variables declared right before the call, surfacing the work done before construction. diff --git a/src/test/java/org/openrewrite/java/migrate/jspecify/RemoveDuplicateAnnotationsTest.java b/src/test/java/org/openrewrite/java/migrate/jspecify/RemoveDuplicateAnnotationsTest.java deleted file mode 100644 index 03f8a98b0c..0000000000 --- a/src/test/java/org/openrewrite/java/migrate/jspecify/RemoveDuplicateAnnotationsTest.java +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright 2026 the original author or authors. - *

- * Licensed under the Moderne Source Available License (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * https://docs.moderne.io/licensing/moderne-source-available-license - *

- * 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.migrate.jspecify; - -import org.junit.jupiter.api.Test; -import org.openrewrite.DocumentExample; -import org.openrewrite.Issue; -import org.openrewrite.java.JavaParser; -import org.openrewrite.test.RecipeSpec; -import org.openrewrite.test.RewriteTest; - -import static org.openrewrite.java.Assertions.java; - -@Issue("https://github.com/openrewrite/rewrite-migrate-java/issues/1199") -class RemoveDuplicateAnnotationsTest implements RewriteTest { - - @Override - public void defaults(RecipeSpec spec) { - spec - .recipe(new RemoveDuplicateAnnotations("org.jspecify.annotations.*")) - .parser(JavaParser.fromJavaVersion().classpath("jspecify")); - } - - @DocumentExample - @Test - void removeDuplicateAnnotationOnMethodParameter() { - rewriteRun( - //language=java - java( - """ - import org.jspecify.annotations.Nullable; - - class Foo { - public void add(@Nullable @org.jspecify.annotations.Nullable final String bar) { - } - } - """, - """ - import org.jspecify.annotations.Nullable; - - class Foo { - public void add(@Nullable final String bar) { - } - } - """ - ) - ); - } - - @Test - void removeDuplicateAnnotationOnField() { - rewriteRun( - //language=java - java( - """ - import org.jspecify.annotations.Nullable; - - class Foo { - @Nullable - @Nullable - private String bar; - } - """, - """ - import org.jspecify.annotations.Nullable; - - class Foo { - @Nullable - private String bar; - } - """ - ) - ); - } - - @Test - void removeDuplicateAnnotationOnMethodReturnType() { - rewriteRun( - //language=java - java( - """ - import org.jspecify.annotations.Nullable; - - class Foo { - @Nullable - @Nullable - public String bar() { - return null; - } - } - """, - """ - import org.jspecify.annotations.Nullable; - - class Foo { - @Nullable - public String bar() { - return null; - } - } - """ - ) - ); - } - - @Test - void removeDuplicateAnnotationOnClass() { - rewriteRun( - //language=java - java( - """ - import org.jspecify.annotations.NullMarked; - - @NullMarked - @NullMarked - class Foo { - } - """, - """ - import org.jspecify.annotations.NullMarked; - - @NullMarked - class Foo { - } - """ - ) - ); - } - - @Test - void removeDuplicateTypeUseAnnotation() { - rewriteRun( - //language=java - java( - """ - import java.util.List; - import org.jspecify.annotations.Nullable; - - class Foo { - private List<@Nullable @Nullable String> bar; - } - """, - """ - import java.util.List; - import org.jspecify.annotations.Nullable; - - class Foo { - private List<@Nullable String> bar; - } - """ - ) - ); - } - - @Test - void retainSingleAnnotation() { - rewriteRun( - //language=java - java( - """ - import org.jspecify.annotations.NonNull; - import org.jspecify.annotations.Nullable; - - class Foo { - public @Nullable String bar(@NonNull String baz) { - return null; - } - } - """ - ) - ); - } - - @Test - void retainDistinctAnnotations() { - rewriteRun( - //language=java - java( - """ - import org.jspecify.annotations.NonNull; - import org.jspecify.annotations.Nullable; - - class Foo { - public void bar(@Nullable @NonNull String baz) { - } - } - """ - ) - ); - } - - @Test - void retainAnnotationsOfOtherTypes() { - rewriteRun( - //language=java - java( - """ - class Foo { - @SuppressWarnings("all") - public void bar() { - } - } - """ - ) - ); - } -}