From 4b896f45abc5ffec69abfe96b0dcd81db9e113dc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 20 Aug 2025 00:48:46 +0000 Subject: [PATCH 1/3] Initial plan From a04bb952b5d79d9048f63fc4cfe8d8b9b8996dcf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 20 Aug 2025 01:00:14 +0000 Subject: [PATCH 2/3] Add initial Minecraft version migration recipe with basic functionality Co-authored-by: TimeBather <25410512+TimeBather@users.noreply.github.com> --- build.gradle.kts | 2 + .../com/yourorg/AppendToReleaseNotes.java | 109 ------- .../com/yourorg/AssertEqualsToAssertThat.java | 89 ------ src/main/java/com/yourorg/ClassHierarchy.java | 73 ----- .../java/com/yourorg/EqualsAvoidsNull.java | 68 ----- .../java/com/yourorg/FindSpringBeans.java | 69 ----- .../yourorg/MinecraftVersionMigration.java | 101 +++++++ .../com/yourorg/NoCollectionMutation.java | 154 ---------- .../com/yourorg/NoGuavaListsNewArrayList.java | 119 -------- .../java/com/yourorg/SimplifyTernary.java | 62 ---- src/main/java/com/yourorg/StringIsEmpty.java | 22 -- src/main/java/com/yourorg/TrackJavaTodos.java | 50 ---- src/main/java/com/yourorg/TrackTodos.java | 55 ---- .../com/yourorg/UpdateConcoursePipeline.java | 92 ------ .../java/com/yourorg/UseIntegerValueOf.java | 34 --- .../com/yourorg/UseSdkManJavaVersion.java | 96 ------- src/main/java/com/yourorg/package-info.java | 12 +- .../yourorg/table/ClassHierarchyReport.java | 50 ---- .../com/yourorg/table/SpringBeansReport.java | 42 --- .../com/yourorg/table/TodoCommentsReport.java | 30 -- .../java/com/yourorg/trait/TodoComment.java | 51 ---- .../com/yourorg/AppendToReleaseNotesTest.java | 67 ----- .../yourorg/AssertEqualsToAssertThatTest.java | 89 ------ .../java/com/yourorg/ClassHierarchyTest.java | 88 ------ .../com/yourorg/EqualsAvoidsNullTest.java | 96 ------- .../java/com/yourorg/FindSpringBeansTest.java | 112 -------- .../MinecraftVersionMigrationTest.java | 179 ++++++++++++ .../com/yourorg/NoCollectionMutationTest.java | 226 --------------- .../yourorg/NoGuavaListsNewArrayListTest.java | 180 ------------ .../java/com/yourorg/SimplifyTernaryTest.java | 116 -------- .../java/com/yourorg/StringIsEmptyTest.java | 170 ----------- .../java/com/yourorg/TrackJavaTodosTest.java | 120 -------- src/test/java/com/yourorg/TrackTodosTest.java | 268 ------------------ .../yourorg/UpdateConcoursePipelineTest.java | 57 ---- .../com/yourorg/UseApacheStringUtilsTest.java | 83 ------ .../com/yourorg/UseIntegerValueOfTest.java | 70 ----- .../yourorg/UseOpenRewriteNullableTest.java | 65 ----- .../com/yourorg/UseSdkManJavaVersionTest.java | 253 ----------------- 38 files changed, 287 insertions(+), 3332 deletions(-) delete mode 100644 src/main/java/com/yourorg/AppendToReleaseNotes.java delete mode 100644 src/main/java/com/yourorg/AssertEqualsToAssertThat.java delete mode 100644 src/main/java/com/yourorg/ClassHierarchy.java delete mode 100644 src/main/java/com/yourorg/EqualsAvoidsNull.java delete mode 100644 src/main/java/com/yourorg/FindSpringBeans.java create mode 100644 src/main/java/com/yourorg/MinecraftVersionMigration.java delete mode 100644 src/main/java/com/yourorg/NoCollectionMutation.java delete mode 100644 src/main/java/com/yourorg/NoGuavaListsNewArrayList.java delete mode 100644 src/main/java/com/yourorg/SimplifyTernary.java delete mode 100644 src/main/java/com/yourorg/StringIsEmpty.java delete mode 100644 src/main/java/com/yourorg/TrackJavaTodos.java delete mode 100644 src/main/java/com/yourorg/TrackTodos.java delete mode 100644 src/main/java/com/yourorg/UpdateConcoursePipeline.java delete mode 100644 src/main/java/com/yourorg/UseIntegerValueOf.java delete mode 100644 src/main/java/com/yourorg/UseSdkManJavaVersion.java delete mode 100644 src/main/java/com/yourorg/table/ClassHierarchyReport.java delete mode 100644 src/main/java/com/yourorg/table/SpringBeansReport.java delete mode 100644 src/main/java/com/yourorg/table/TodoCommentsReport.java delete mode 100644 src/main/java/com/yourorg/trait/TodoComment.java delete mode 100644 src/test/java/com/yourorg/AppendToReleaseNotesTest.java delete mode 100644 src/test/java/com/yourorg/AssertEqualsToAssertThatTest.java delete mode 100644 src/test/java/com/yourorg/ClassHierarchyTest.java delete mode 100644 src/test/java/com/yourorg/EqualsAvoidsNullTest.java delete mode 100644 src/test/java/com/yourorg/FindSpringBeansTest.java create mode 100644 src/test/java/com/yourorg/MinecraftVersionMigrationTest.java delete mode 100644 src/test/java/com/yourorg/NoCollectionMutationTest.java delete mode 100644 src/test/java/com/yourorg/NoGuavaListsNewArrayListTest.java delete mode 100644 src/test/java/com/yourorg/SimplifyTernaryTest.java delete mode 100644 src/test/java/com/yourorg/StringIsEmptyTest.java delete mode 100644 src/test/java/com/yourorg/TrackJavaTodosTest.java delete mode 100644 src/test/java/com/yourorg/TrackTodosTest.java delete mode 100644 src/test/java/com/yourorg/UpdateConcoursePipelineTest.java delete mode 100644 src/test/java/com/yourorg/UseApacheStringUtilsTest.java delete mode 100644 src/test/java/com/yourorg/UseIntegerValueOfTest.java delete mode 100644 src/test/java/com/yourorg/UseOpenRewriteNullableTest.java delete mode 100644 src/test/java/com/yourorg/UseSdkManJavaVersionTest.java diff --git a/build.gradle.kts b/build.gradle.kts index 9fe8258..6923087 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -25,6 +25,8 @@ dependencies { implementation("org.openrewrite.recipe:rewrite-java-dependencies") implementation("org.openrewrite:rewrite-yaml") implementation("org.openrewrite:rewrite-xml") + implementation("org.openrewrite:rewrite-gradle") + implementation("org.openrewrite:rewrite-properties") implementation("org.openrewrite.meta:rewrite-analysis") implementation("org.assertj:assertj-core:latest.release") runtimeOnly("org.openrewrite:rewrite-java-17") diff --git a/src/main/java/com/yourorg/AppendToReleaseNotes.java b/src/main/java/com/yourorg/AppendToReleaseNotes.java deleted file mode 100644 index bbb1d49..0000000 --- a/src/main/java/com/yourorg/AppendToReleaseNotes.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * 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 com.yourorg; - -import lombok.EqualsAndHashCode; -import lombok.Value; -import org.jspecify.annotations.Nullable; -import org.openrewrite.*; -import org.openrewrite.text.PlainText; -import org.openrewrite.text.PlainTextParser; -import org.openrewrite.text.PlainTextVisitor; - -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Collection; - -import static java.util.Collections.emptyList; -import static java.util.stream.Collectors.toList; - -@Value -@EqualsAndHashCode(callSuper = false) -public class AppendToReleaseNotes extends ScanningRecipe { - - @Override - public String getDisplayName() { - return "Append to release notes"; - } - - @Override - public String getDescription() { - return "Adds the specified line to RELEASE.md."; - } - - @Option(displayName = "Message", - description = "Message to append to the bottom of RELEASE.md.", - example = "## 1.0.0\n\n- New feature") - String message; - - // The shared state between the scanner and the visitor. The custom class ensures we can easily extend the recipe. - public static class Accumulator { - boolean found; - } - - @Override - public Accumulator getInitialValue(ExecutionContext ctx) { - return new Accumulator(); - } - - @Override - public TreeVisitor getScanner(Accumulator acc) { - return new TreeVisitor() { - @Override - public @Nullable Tree visit(@Nullable Tree tree, ExecutionContext ctx) { - if (tree instanceof SourceFile) { - Path sourcePath = ((SourceFile) tree).getSourcePath(); - acc.found |= "RELEASE.md".equals(sourcePath.toString()); - } - return tree; - } - }; - } - - @Override - public Collection generate(Accumulator acc, ExecutionContext ctx) { - if (acc.found) { - return emptyList(); - } - // If the file was not found, create it - return PlainTextParser.builder().build() - // We start with an empty string that we then append to in the visitor - .parse("") - // Be sure to set the source path for any generated file to specify where to put it when the recipe run is completed - .map(it -> (SourceFile) it.withSourcePath(Paths.get("RELEASE.md"))) - .collect(toList()); - } - - @Override - public TreeVisitor getVisitor(Accumulator acc) { - return new PlainTextVisitor() { - @Override - public PlainText visitText(PlainText text, ExecutionContext ctx) { - PlainText t = super.visitText(text, ctx); - // If the file is not RELEASE.md, don't modify it - if (!"RELEASE.md".equals(t.getSourcePath().toString())) { - return t; - } - // If the file already contains the message, don't append it again - if (t.getText().contains(message)) { - return t; - } - // Append the message to the end of the file - return t.withText(t.getText() + "\n" + message); - } - }; - } -} diff --git a/src/main/java/com/yourorg/AssertEqualsToAssertThat.java b/src/main/java/com/yourorg/AssertEqualsToAssertThat.java deleted file mode 100644 index 9ecf067..0000000 --- a/src/main/java/com/yourorg/AssertEqualsToAssertThat.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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 com.yourorg; - -import lombok.EqualsAndHashCode; -import lombok.Value; -import org.openrewrite.ExecutionContext; -import org.openrewrite.Preconditions; -import org.openrewrite.Recipe; -import org.openrewrite.TreeVisitor; -import org.openrewrite.java.JavaIsoVisitor; -import org.openrewrite.java.JavaParser; -import org.openrewrite.java.JavaTemplate; -import org.openrewrite.java.MethodMatcher; -import org.openrewrite.java.search.UsesType; -import org.openrewrite.java.tree.Expression; -import org.openrewrite.java.tree.J; - -import java.util.List; - -@Value -@EqualsAndHashCode(callSuper = false) -public class AssertEqualsToAssertThat extends Recipe { - @Override - public String getDisplayName() { - // language=markdown - return "JUnit `assertEquals()` to Assertj `assertThat()`"; - } - - @Override - public String getDescription() { - return "Use AssertJ assertThat instead of JUnit assertEquals()."; - } - - private static final MethodMatcher MATCHER = new MethodMatcher("org.junit.jupiter.api.Assertions assertEquals(..)"); - - @Override - public TreeVisitor getVisitor() { - return Preconditions.check(new UsesType<>("org.junit.jupiter.api.Assertions", null), - new JavaIsoVisitor() { - @Override - public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { - J.MethodInvocation m = super.visitMethodInvocation(method, ctx); - if (!MATCHER.matches(m)) { - return m; - } - List arguments = m.getArguments(); - maybeRemoveImport("org.junit.jupiter.api.Assertions"); - maybeAddImport("org.assertj.core.api.Assertions"); - if (arguments.size() == 2) { - Expression expected = arguments.get(0); - Expression actual = arguments.get(1); - - m = JavaTemplate.builder("Assertions.assertThat(#{any()}).isEqualTo(#{any()})") - .imports("org.assertj.core.api.Assertions") - .javaParser(JavaParser.fromJavaVersion() - .classpath("assertj-core")) - .build() - .apply(getCursor(), m.getCoordinates().replace(), actual, expected); - } else if (arguments.size() == 3) { - Expression expected = arguments.get(0); - Expression actual = arguments.get(1); - Expression description = arguments.get(2); - - m = JavaTemplate.builder("Assertions.assertThat(#{any()}).as(#{any()}).isEqualTo(#{any()})") - .imports("org.assertj.core.api.Assertions") - .javaParser(JavaParser.fromJavaVersion() - .classpath("assertj-core")) - .build() - .apply(getCursor(), m.getCoordinates().replace(), actual, description, expected); - } - return m; - } - }); - } -} diff --git a/src/main/java/com/yourorg/ClassHierarchy.java b/src/main/java/com/yourorg/ClassHierarchy.java deleted file mode 100644 index 645b0e3..0000000 --- a/src/main/java/com/yourorg/ClassHierarchy.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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 com.yourorg; - -import com.yourorg.table.ClassHierarchyReport; -import lombok.EqualsAndHashCode; -import lombok.Value; -import org.openrewrite.ExecutionContext; -import org.openrewrite.Recipe; -import org.openrewrite.TreeVisitor; -import org.openrewrite.java.JavaIsoVisitor; -import org.openrewrite.java.tree.J; -import org.openrewrite.java.tree.JavaType; - -@Value -@EqualsAndHashCode(callSuper = false) -public class ClassHierarchy extends Recipe { - - transient ClassHierarchyReport report = new ClassHierarchyReport(this); - - @Override - public String getDisplayName() { - return "Class hierarchy"; - } - - @Override - public String getDescription() { - return "Produces a data table showing inheritance relationships between classes."; - } - - @Override - public TreeVisitor getVisitor() { - return new JavaIsoVisitor() { - - @Override - public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) { - JavaType.FullyQualified type = classDecl.getType(); - // Capture all classes, which all extend java.lang.Object - if (type instanceof JavaType.Class && type.getSupertype() != null) { - JavaType.FullyQualified supertype = type.getSupertype(); - // Capture the direct superclass - report.insertRow(ctx, new ClassHierarchyReport.Row( - type.getFullyQualifiedName(), - ClassHierarchyReport.Relationship.EXTENDS, - supertype.getFullyQualifiedName())); - - // Capture all interfaces - for (JavaType.FullyQualified anInterface : type.getInterfaces()) { - report.insertRow(ctx, new ClassHierarchyReport.Row( - type.getFullyQualifiedName(), - ClassHierarchyReport.Relationship.IMPLEMENTS, - anInterface.getFullyQualifiedName() - )); - } - } - return super.visitClassDeclaration(classDecl, ctx); - } - }; - } -} diff --git a/src/main/java/com/yourorg/EqualsAvoidsNull.java b/src/main/java/com/yourorg/EqualsAvoidsNull.java deleted file mode 100644 index 10c1696..0000000 --- a/src/main/java/com/yourorg/EqualsAvoidsNull.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2025 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 com.yourorg; - -import com.google.errorprone.refaster.annotation.AfterTemplate; -import com.google.errorprone.refaster.annotation.BeforeTemplate; -import org.jspecify.annotations.Nullable; -import org.openrewrite.java.template.Matcher; -import org.openrewrite.java.template.Matches; -import org.openrewrite.java.template.NotMatches; -import org.openrewrite.java.template.RecipeDescriptor; -import org.openrewrite.java.tree.J; - -@RecipeDescriptor( - name = "EqualsAvoidNull", - description = "Checks that any combination of String literals is on the left side of an equals() comparison.", - tags = {"checkstyle", "null-safety"}) -// https://checkstyle.sourceforge.io/checks/coding/equalsavoidnull.html -public class EqualsAvoidsNull { - - @BeforeTemplate - boolean beforeEquals( - @Matches(LiteralMatcher.class) - String literal, - @NotMatches(LiteralMatcher.class) - String actual) { - // When `actual` is `null` this throws a NullPointerException! - return actual.equals(literal); - } - - @BeforeTemplate - boolean beforeNotNullEquals( - @Matches(LiteralMatcher.class) - String literal, - @Nullable String actual) { - // The null check makes this safer, but cumbersome to read and write - return actual != null && actual.equals(literal); - } - - @AfterTemplate - boolean after(String literal, String actual) { - // Better to put the literal on the left side - return literal.equals(actual); - } - - /** - * Only match literals, not variables or method calls. - */ - public static class LiteralMatcher implements Matcher { - @Override - public boolean matches(J j) { - return j instanceof J.Literal; - } - } -} diff --git a/src/main/java/com/yourorg/FindSpringBeans.java b/src/main/java/com/yourorg/FindSpringBeans.java deleted file mode 100644 index 917ef60..0000000 --- a/src/main/java/com/yourorg/FindSpringBeans.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2025 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 com.yourorg; - -import com.yourorg.table.SpringBeansReport; -import lombok.EqualsAndHashCode; -import lombok.Value; -import org.openrewrite.ExecutionContext; -import org.openrewrite.Recipe; -import org.openrewrite.TreeVisitor; -import org.openrewrite.java.trait.Annotated; -import org.openrewrite.java.trait.Literal; -import org.openrewrite.java.tree.J; -import org.openrewrite.java.tree.JavaSourceFile; -import org.openrewrite.marker.SearchResult; - -@Value -@EqualsAndHashCode(callSuper = false) -public class FindSpringBeans extends Recipe { - - transient SpringBeansReport beansTable = new SpringBeansReport(this); - - @Override - public String getDisplayName() { - return "Find Spring beans"; - } - - @Override - public String getDescription() { - //language=markdown - return "Find all Spring bean names used in your application."; - } - - @Override - public TreeVisitor getVisitor() { - // We'll use a trait here. A trait is an arbitrary concept in your code that has a name, like "a global variable" or "a spring bean". - // Use the existing `org.openrewrite.java.trait.Annotated` trait to easily match annotations, and annotated elements - // In this case we need the values of the `@Bean` annotations - return new Annotated.Matcher("@org.springframework.context.annotation.Bean") - // Convert the trait into a visitor to get access to the annotations, and use their attributes - .asVisitor((annotated, ctx) -> { - // Get name value from the annotation or the default string value if no named annotation arguments are mentioned - String beanName = annotated.getDefaultAttribute("name") - .map(Literal::getString) - // If no value is present in the annotation, we fall back to the method name - .orElseGet(() -> annotated.getCursor().getParentTreeCursor().getValue().getSimpleName()); - - // Insert the bean name into the SpringBeans report - String sourcePath = annotated.getCursor().firstEnclosingOrThrow(JavaSourceFile.class).getSourcePath().toString(); - beansTable.insertRow(ctx, new SpringBeansReport.Row(sourcePath, beanName)); - - // Return a modified LST element with an added search result marker calling out the bean name - return SearchResult.found(annotated.getTree(), beanName); - }); - } -} diff --git a/src/main/java/com/yourorg/MinecraftVersionMigration.java b/src/main/java/com/yourorg/MinecraftVersionMigration.java new file mode 100644 index 0000000..a6aaa57 --- /dev/null +++ b/src/main/java/com/yourorg/MinecraftVersionMigration.java @@ -0,0 +1,101 @@ +/* + * 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 com.yourorg; + +import lombok.EqualsAndHashCode; +import lombok.Value; +import org.openrewrite.*; +import org.openrewrite.groovy.GroovyIsoVisitor; +import org.openrewrite.java.tree.J; +import org.openrewrite.properties.ChangePropertyValue; + +import java.util.regex.Pattern; + +@Value +@EqualsAndHashCode(callSuper = false) +public class MinecraftVersionMigration extends Recipe { + + @Override + public String getDisplayName() { + return "Minecraft version migration"; + } + + @Override + public String getDescription() { + return "Migrates Minecraft project from current version to target version by updating build.gradle and gradle.properties files."; + } + + @Option(displayName = "Current version", + description = "The current Minecraft version to migrate from.", + example = "1.20.1") + String currentVersion; + + @Option(displayName = "Target version", + description = "The target Minecraft version to migrate to.", + example = "1.21") + String targetVersion; + + @Override + public TreeVisitor getVisitor() { + return Preconditions.or( + // Handle build.gradle files + Preconditions.check( + new FindSourceFiles("**/build.gradle"), + new GroovyIsoVisitor() { + @Override + public J.Literal visitLiteral(J.Literal literal, ExecutionContext ctx) { + J.Literal l = super.visitLiteral(literal, ctx); + if (l.getValue() instanceof String) { + String value = (String) l.getValue(); + if (containsMinecraftVersion(value)) { + String updatedValue = value.replace(currentVersion, targetVersion); + if (!updatedValue.equals(value)) { + return l.withValue(updatedValue).withValueSource("\"" + updatedValue + "\""); + } + } + } + return l; + } + } + ), + // Handle gradle.properties files - basic version properties + Preconditions.check( + new FindSourceFiles("**/gradle.properties"), + new ChangePropertyValue("minecraft_version", targetVersion, null, null, null).getVisitor() + ), + // Handle more gradle.properties patterns + Preconditions.check( + new FindSourceFiles("**/gradle.properties"), + new ChangePropertyValue("mc_version", targetVersion, null, null, null).getVisitor() + ) + ); + } + + private boolean containsMinecraftVersion(String value) { + // Check if the string contains version patterns commonly used in Minecraft projects + return value.contains(currentVersion) && ( + value.toLowerCase().contains("minecraft") || + value.toLowerCase().contains("forge") || + value.toLowerCase().contains("neoforge") || + value.toLowerCase().contains("fabric") || + value.toLowerCase().contains("quilt") || + // Version patterns like "net.minecraft:server:1.20.1" + Pattern.compile("net\\.minecraft[\\w.]*:" + Pattern.quote(currentVersion)).matcher(value).find() || + // Version patterns in dependency strings + Pattern.compile(":['\"]?" + Pattern.quote(currentVersion) + "['\"]?").matcher(value).find() + ); + } +} \ No newline at end of file diff --git a/src/main/java/com/yourorg/NoCollectionMutation.java b/src/main/java/com/yourorg/NoCollectionMutation.java deleted file mode 100644 index 8f51484..0000000 --- a/src/main/java/com/yourorg/NoCollectionMutation.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * 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 com.yourorg; - -import fj.data.Option; -import lombok.EqualsAndHashCode; -import lombok.Value; -import org.jspecify.annotations.Nullable; -import org.openrewrite.*; -import org.openrewrite.analysis.dataflow.DataFlowNode; -import org.openrewrite.analysis.dataflow.DataFlowSpec; -import org.openrewrite.analysis.dataflow.Dataflow; -import org.openrewrite.java.JavaTemplate; -import org.openrewrite.java.JavaVisitor; -import org.openrewrite.java.MethodMatcher; -import org.openrewrite.java.search.UsesType; -import org.openrewrite.java.tree.J; -import org.openrewrite.java.tree.JavaType; -import org.openrewrite.java.tree.TypeUtils; - -@Value -@EqualsAndHashCode(callSuper = false) -public class NoCollectionMutation extends Recipe { - @Override - public String getDisplayName() { - return "Prevent LST collection mutation"; - } - - @Override - public String getDescription() { - return "LST elements should always be treated as immutable, even for fields that are not protected from mutation at runtime. " + - "Adding or removing an element from a collection on an LST element is always a bug. " + - "This recipe uses Dataflow analysis to detect and put defensive copies around collection mutations."; - } - - private static final MethodMatcher ADD_MATCHER = new MethodMatcher("java.util.List add(..)"); - private static final MethodMatcher ADD_ALL_MATCHER = new MethodMatcher("java.util.List addAll(..)"); - private static final MethodMatcher CLEAR_MATCHER = new MethodMatcher("java.util.List clear()"); - private static final MethodMatcher REMOVE_MATCHER = new MethodMatcher("java.util.List remove(..)"); - private static final MethodMatcher REMOVE_ALL_MATCHER = new MethodMatcher("java.util.List removeAll(..)"); - private static final MethodMatcher REPLACE_MATCHER = new MethodMatcher("java.util.List replace(..)"); - private static final MethodMatcher SET_MATCHER = new MethodMatcher("java.util.List set(..)"); - private static final MethodMatcher SORT_MATCHER = new MethodMatcher("java.util.List sort(..)"); - - /** - * The "select" of a method is the receiver or target of the invocation. In the method call "aList.add(foo)" the "select" is "aList". - * - * @param cursor a stack of LST elements with parent/child relationships connecting an individual LST element to the root of the tree - * @return true if the cursor points to the "select" of a method invocation that is a list mutation - */ - private static boolean isListMutationSelect(Cursor cursor) { - Object parentValue = cursor.getParentTreeCursor().getValue(); - if (!(parentValue instanceof J.MethodInvocation) || - ((J.MethodInvocation) parentValue).getMethodType() == null || - ((J.MethodInvocation) parentValue).getSelect() != cursor.getValue()) { - return false; - } - JavaType.Method mt = ((J.MethodInvocation) parentValue).getMethodType(); - return ADD_MATCHER.matches(mt) || - ADD_ALL_MATCHER.matches(mt) || - CLEAR_MATCHER.matches(mt) || - REMOVE_MATCHER.matches(mt) || - REMOVE_ALL_MATCHER.matches(mt) || - REPLACE_MATCHER.matches(mt) || - SET_MATCHER.matches(mt) || - SORT_MATCHER.matches(mt); - } - - private static final MethodMatcher NEW_ARRAY_LIST_MATCHER = new MethodMatcher("java.util.ArrayList (java.util.Collection)"); - - /** - * @param cursor a stack of LST elements with parent/child relationships connecting an individual LST element to the root of the tree - * @return true if the cursor points to an LST element contained within the argument list of a constructor or - * function which creates a defensive copy as needed - */ - private static boolean inDefensiveCopy(@Nullable Cursor cursor) { - if (cursor == null) { - return false; - } - Object value = cursor.getValue(); - if (value instanceof J.NewClass && NEW_ARRAY_LIST_MATCHER.matches(((J.NewClass) value).getMethodType())) { - return true; - } - return inDefensiveCopy(cursor.getParent()); - } - - @Override - public TreeVisitor getVisitor() { - return Preconditions.check( - Preconditions.or( - new UsesType<>("org.openrewrite.Tree", true), - new UsesType<>("java.util.List", true)), - new JavaVisitor() { - @Override - public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { - J j = super.visitMethodInvocation(method, ctx); - if (!(j instanceof J.MethodInvocation)) { - return j; - } - J.MethodInvocation m = (J.MethodInvocation) j; - if (m.getMethodType() == null || !(m.getMethodType().getDeclaringType() instanceof JavaType.Class)) { - return m; - } - JavaType.Method mt = m.getMethodType(); - JavaType.Class declaringType = (JavaType.Class) mt.getDeclaringType(); - if (!TypeUtils.isAssignableTo("org.openrewrite.Tree", declaringType) || !TypeUtils.isAssignableTo("java.util.List", mt.getReturnType())) { - return m; - } - - boolean isMutated = Dataflow.startingAt(getCursor()).findSinks(new DataFlowSpec() { - @Override - public boolean isSource(DataFlowNode srcNode) { - return true; - } - - @Override - public boolean isSink(DataFlowNode sinkNode) { - return isListMutationSelect(sinkNode.getCursor()); - } - }).bind(sinkFlow -> { - for (Cursor sink : sinkFlow.getSinkCursors()) { - if (!inDefensiveCopy(sink)) { - return Option.some(sink); - } - } - return Option.none(); - }) - .isSome(); - if (!isMutated) { - return m; - } - - maybeAddImport("java.util.ArrayList"); - return JavaTemplate.builder("new ArrayList<>(#{any(java.util.List)})") - .imports("java.util.ArrayList") - .build() - .apply(getCursor(), m.getCoordinates().replace(), m); - } - }); - } -} diff --git a/src/main/java/com/yourorg/NoGuavaListsNewArrayList.java b/src/main/java/com/yourorg/NoGuavaListsNewArrayList.java deleted file mode 100644 index cd68351..0000000 --- a/src/main/java/com/yourorg/NoGuavaListsNewArrayList.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2021 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 com.yourorg; - -import lombok.EqualsAndHashCode; -import lombok.Value; -import org.openrewrite.ExecutionContext; -import org.openrewrite.Preconditions; -import org.openrewrite.Recipe; -import org.openrewrite.TreeVisitor; -import org.openrewrite.java.JavaTemplate; -import org.openrewrite.java.JavaVisitor; -import org.openrewrite.java.MethodMatcher; -import org.openrewrite.java.TreeVisitingPrinter; -import org.openrewrite.java.search.UsesMethod; -import org.openrewrite.java.tree.J; - -@Value -@EqualsAndHashCode(callSuper = false) -public class NoGuavaListsNewArrayList extends Recipe { - // These matchers use a syntax described on https://docs.openrewrite.org/reference/method-patterns - private static final MethodMatcher NEW_ARRAY_LIST = new MethodMatcher("com.google.common.collect.Lists newArrayList()"); - private static final MethodMatcher NEW_ARRAY_LIST_ITERABLE = new MethodMatcher("com.google.common.collect.Lists newArrayList(java.lang.Iterable)"); - private static final MethodMatcher NEW_ARRAY_LIST_CAPACITY = new MethodMatcher("com.google.common.collect.Lists newArrayListWithCapacity(int)"); - - @Override - public String getDisplayName() { - //language=markdown - return "Use `new ArrayList<>()` instead of Guava"; - } - - @Override - public String getDescription() { - //language=markdown - return "Prefer the Java standard library over third-party usage of Guava in simple cases like this."; - } - - @Override - public TreeVisitor getVisitor() { - return Preconditions.check( - // Any change to the AST made by the preconditions check will lead to the visitor returned by Recipe - // .getVisitor() being applied - // No changes made by the preconditions check will be kept - Preconditions.or( - new UsesMethod<>(NEW_ARRAY_LIST), - new UsesMethod<>(NEW_ARRAY_LIST_ITERABLE), - new UsesMethod<>(NEW_ARRAY_LIST_CAPACITY)), - // To avoid stale state persisting between cycles, getVisitor() should always return a new instance of - // its visitor - new JavaVisitor() { - // Java Templates are used to generate Java code easily. - // They use a syntax that expand Java with possible type-safe insertions points. - // See https://docs.openrewrite.org/concepts-and-explanations/javatemplate for full documentation - private final JavaTemplate newArrayList = JavaTemplate.builder("new ArrayList<>()") - .imports("java.util.ArrayList") - .build(); - - private final JavaTemplate newArrayListIterable = - JavaTemplate.builder("new ArrayList<>(#{any(java.util.Collection)})") - .imports("java.util.ArrayList") - .build(); - - private final JavaTemplate newArrayListCapacity = - JavaTemplate.builder("new ArrayList<>(#{any(int)})") - .imports("java.util.ArrayList") - .build(); - - // This method override is only here to show how to print the AST for debugging purposes. - // You can remove this method if you don't need it. - @Override - public J visitCompilationUnit(J.CompilationUnit cu, ExecutionContext ctx) { - // This is a useful debugging tool if you're ever unsure what the visitor is visiting - String printed = TreeVisitingPrinter.printTree(cu); - System.out.printf(printed); - - // You must always delegate to the super method to ensure the visitor continues to visit deeper - // return cu; // this leads to a recipe that makes no changes at all - return super.visitCompilationUnit(cu, ctx); - } - - // Visit any method invocation, and replace matches with the new ArrayList instantiation. - @Override - public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { - if (NEW_ARRAY_LIST.matches( method )) { - maybeRemoveImport( "com.google.common.collect.Lists" ); - maybeAddImport( "java.util.ArrayList" ); - return newArrayList.apply( getCursor(), method.getCoordinates().replace() ); - } - if (NEW_ARRAY_LIST_ITERABLE.matches( method )) { - maybeRemoveImport( "com.google.common.collect.Lists" ); - maybeAddImport( "java.util.ArrayList" ); - return newArrayListIterable.apply( getCursor(), method.getCoordinates().replace(), - method.getArguments().get( 0 ) ); - } - if (NEW_ARRAY_LIST_CAPACITY.matches( method )) { - maybeRemoveImport( "com.google.common.collect.Lists" ); - maybeAddImport( "java.util.ArrayList" ); - return newArrayListCapacity.apply( getCursor(), method.getCoordinates().replace(), - method.getArguments().get( 0 ) ); - } - return super.visitMethodInvocation( method, ctx ); - } - } - ); - } -} diff --git a/src/main/java/com/yourorg/SimplifyTernary.java b/src/main/java/com/yourorg/SimplifyTernary.java deleted file mode 100644 index 0fef935..0000000 --- a/src/main/java/com/yourorg/SimplifyTernary.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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 com.yourorg; - -import com.google.errorprone.refaster.annotation.AfterTemplate; -import com.google.errorprone.refaster.annotation.BeforeTemplate; -import org.openrewrite.java.template.RecipeDescriptor; - -@SuppressWarnings({"SimplifiableConditionalExpression", "unused"}) -@RecipeDescriptor( - name = "Simplify ternary expressions", - description = "Simplifies various types of ternary expressions to improve code readability." -) -public class SimplifyTernary { // This class should not extend Recipe; a generated class will extend Recipe instead - - @RecipeDescriptor( - name = "Replace `booleanExpression ? true : false` with `booleanExpression`", - description = "Replace ternary expressions like `booleanExpression ? true : false` with `booleanExpression`." - ) - public static class SimplifyTernaryTrueFalse { - - @BeforeTemplate - boolean before(boolean expr) { - return expr ? true : false; - } - - @AfterTemplate - boolean after(boolean expr) { - return expr; - } - } - - @RecipeDescriptor( - name = "Replace `booleanExpression ? false : true` with `!booleanExpression`", - description = "Replace ternary expressions like `booleanExpression ? false : true` with `!booleanExpression`." - ) - public static class SimplifyTernaryFalseTrue { - - @BeforeTemplate - boolean before(boolean expr) { - return expr ? false : true; - } - - @AfterTemplate - boolean after(boolean expr) { - return !expr; - } - } -} diff --git a/src/main/java/com/yourorg/StringIsEmpty.java b/src/main/java/com/yourorg/StringIsEmpty.java deleted file mode 100644 index 802c91f..0000000 --- a/src/main/java/com/yourorg/StringIsEmpty.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * 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 com.yourorg; - -// TODO - This is a placeholder for a Refaster recipe. Implement the recipe by adding before and after annotated methods. -// The rule should replace calls to `String.length() == 0` with `String.isEmpty()`, as well as similar variants. -// You're done when all the tests in `StringIsEmptyTest` passes. -public class StringIsEmpty { -} diff --git a/src/main/java/com/yourorg/TrackJavaTodos.java b/src/main/java/com/yourorg/TrackJavaTodos.java deleted file mode 100644 index ec474ea..0000000 --- a/src/main/java/com/yourorg/TrackJavaTodos.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.yourorg; - -import lombok.EqualsAndHashCode; -import lombok.Value; -import org.jspecify.annotations.Nullable; -import org.openrewrite.*; - -import java.util.LinkedHashSet; - -// TODO - This is a placeholder for a scanning recipe. -// Implement a recipe that finds any comments in Java source files that contain `TODO`, and add them to a file called `TODO.md`. -// You're done when all of the tests in `TrackJavaTodosTest` pass. -@Value -@EqualsAndHashCode(callSuper = false) -public class TrackJavaTodos extends ScanningRecipe { - - @Override - public String getDisplayName() { - return "Track TODOs from Java comments"; - } - - @Override - public String getDescription() { - return "Scans Java source comments for TODOs and collects them."; - } - - @Option(displayName = "Header", - description = "Header for TODO.md. Defaults to `## To Do List` if not provided.", - example = "## To Do List", - required = false) - @Nullable - String header; - - public static class TodoComments { - boolean foundTodoFile; - LinkedHashSet todos = new LinkedHashSet<>(); - } - - @Override - public TodoComments getInitialValue(ExecutionContext ctx) { - return new TodoComments(); - } - - @Override - public TreeVisitor getScanner(TodoComments acc) { - return new TreeVisitor() { - - }; - } -} diff --git a/src/main/java/com/yourorg/TrackTodos.java b/src/main/java/com/yourorg/TrackTodos.java deleted file mode 100644 index ba611b2..0000000 --- a/src/main/java/com/yourorg/TrackTodos.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.yourorg; - -import com.yourorg.table.TodoCommentsReport; -import com.yourorg.trait.TodoComment; -import lombok.EqualsAndHashCode; -import lombok.Value; -import org.jspecify.annotations.Nullable; -import org.openrewrite.*; - -import java.util.LinkedHashSet; - -// TODO - This is a placeholder for a scanning recipe that uses traits and data tables. -// Implement a recipe that finds any comments in Java, XML, or YAML source files that contain `TODO`, and add them to a file called `TODO.md`. -// Also store the data in a data table using TodoCommentsReport. -// You're done when all of the tests in `TrackTodosTest` pass. -@Value -@EqualsAndHashCode(callSuper = false) -public class TrackTodos extends ScanningRecipe { - - transient TodoCommentsReport todoCommentsTable = new TodoCommentsReport(this); - - @Override - public String getDisplayName() { - return "Track TODOs from Java, YAML, or XML comments"; - } - - @Override - public String getDescription() { - return "Scans Java, YAML, and XML source comments for TODOs and collects them."; - } - - @Option(displayName = "Header", - description = "Header for TODO.md. Defaults to `## To Do List` if not provided.", - example = "## To Do List", - required = false) - @Nullable - String header; - - public static class TodoComments { - boolean foundTodoFile; - LinkedHashSet todos = new LinkedHashSet<>(); - } - - @Override - public TodoComments getInitialValue(ExecutionContext ctx) { - return new TodoComments(); - } - - @Override - public TreeVisitor getScanner(TodoComments acc) { - return new TreeVisitor() { - - }; - } -} diff --git a/src/main/java/com/yourorg/UpdateConcoursePipeline.java b/src/main/java/com/yourorg/UpdateConcoursePipeline.java deleted file mode 100644 index 00c7d34..0000000 --- a/src/main/java/com/yourorg/UpdateConcoursePipeline.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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 com.yourorg; - -import lombok.EqualsAndHashCode; -import lombok.Value; -import org.openrewrite.*; -import org.openrewrite.yaml.ChangePropertyValue; -import org.openrewrite.yaml.YamlIsoVisitor; -import org.openrewrite.yaml.tree.Yaml; - -@Value -@EqualsAndHashCode(callSuper = false) -public class UpdateConcoursePipeline extends Recipe { - @Override - public String getDisplayName() { - return "Update concourse pipeline"; - } - - @Override - public String getDescription() { - return "Update the tag filter on concourse pipelines."; - } - - @Option(displayName = "New tag filter version", - description = "tag filter version.", - example = "8.2.0") - String version; - - @Override - public TreeVisitor getVisitor() { - return Preconditions.check( - Preconditions.or( - new FindSourceFiles("ci/pipeline*.yml").getVisitor(), - new FindSourceFiles("ci/pipeline*.yaml").getVisitor()), - new YamlIsoVisitor() { - - @Override - public Yaml.Mapping.Entry visitMappingEntry(Yaml.Mapping.Entry entry, ExecutionContext ctx) { - Yaml.Mapping.Entry e = super.visitMappingEntry(entry, ctx); - if ("source".equals(e.getKey().getValue())) { - Yaml.Block value = e.getValue(); - if (!(value instanceof Yaml.Mapping)) { - return e; - } - Yaml.Mapping mapping = (Yaml.Mapping) value; - Yaml.Mapping.Entry uriEntry = null; - Yaml.Mapping.Entry tagFilter = null; - for (Yaml.Mapping.Entry mappingEntry : mapping.getEntries()) { - if ("uri".equals(mappingEntry.getKey().getValue())) { - uriEntry = mappingEntry; - } else if ("tag_filter".equals(mappingEntry.getKey().getValue())) { - tagFilter = mappingEntry; - } - } - if (uriEntry == null || tagFilter == null) { - return e; - } - if (!(uriEntry.getValue() instanceof Yaml.Scalar) || !(tagFilter.getValue() instanceof Yaml.Scalar)) { - return e; - } - Yaml.Scalar uriValue = (Yaml.Scalar) uriEntry.getValue(); - if (!uriValue.getValue().contains(".git")) { - return e; - } - Yaml.Scalar tagFilterValue = (Yaml.Scalar) tagFilter.getValue(); - if (version.equals(tagFilterValue.getValue())) { - return e; - } - return (Yaml.Mapping.Entry) new ChangePropertyValue("source.tag_filter", version, null, null, null, null) - .getVisitor() - .visitNonNull(e, ctx); - } - return e; - } - } - ); - } -} diff --git a/src/main/java/com/yourorg/UseIntegerValueOf.java b/src/main/java/com/yourorg/UseIntegerValueOf.java deleted file mode 100644 index 3904578..0000000 --- a/src/main/java/com/yourorg/UseIntegerValueOf.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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 com.yourorg; - -import org.openrewrite.Recipe; - -// TODO - This is a placeholder for an imperative recipe. -// Implement a recipe that replaces all `new Integer(x)` constructors with Integer.valueOf(x) for int values, or Integer.parseInt(x) for String values. -// You're done when all of the tests in `UseIntegerValueOfTest` pass. -public class UseIntegerValueOf extends Recipe { - - @Override - public String getDisplayName() { - return "Use Integer.valueOf(x) or Integer.parseInt(x) instead of new Integer(x)"; - } - - @Override - public String getDescription() { - return "Replaces unnecessary boxing constructor calls with the more efficient Integer.valueOf(x) for int values, or Integer.parseInt(x) for String values."; - } -} diff --git a/src/main/java/com/yourorg/UseSdkManJavaVersion.java b/src/main/java/com/yourorg/UseSdkManJavaVersion.java deleted file mode 100644 index bbdd90c..0000000 --- a/src/main/java/com/yourorg/UseSdkManJavaVersion.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.yourorg; - -import org.jspecify.annotations.Nullable; -import org.openrewrite.*; -import org.openrewrite.java.marker.JavaVersion; -import org.openrewrite.java.tree.JavaSourceFile; -import org.openrewrite.text.PlainText; -import org.openrewrite.text.PlainTextVisitor; - -import java.nio.file.Paths; -import java.util.Collection; - -import static java.util.Collections.emptyList; -import static java.util.Collections.singletonList; - -public class UseSdkManJavaVersion extends ScanningRecipe { - - @Override - public String getDisplayName() { - return "Use SDKMan java version"; - } - - @Override - public String getDescription() { - //language=Markdown - return "Examines a multi-project build, determines the Java version in use via available markers, and sets up or updates a .sdkmanrc file in the project root."; - } - - @Override - public Accumulator getInitialValue(ExecutionContext ctx) { - return new Accumulator(); - } - - public static class Accumulator { - boolean sdkmanrcExists = false; - int javaVersion = -1; - } - - @Override - public TreeVisitor getScanner(Accumulator acc) { - return new TreeVisitor() { - @Override - public @Nullable Tree visit(@Nullable Tree tree, ExecutionContext ctx) { - if (tree instanceof PlainText) { - // Check if the current tree is a PlainText file, which could be a .sdkmanrc file - acc.sdkmanrcExists |= ((PlainText) tree).getSourcePath().endsWith(".sdkmanrc"); - } else if (tree instanceof JavaSourceFile) { - // we have a java file which usually has the project, source set and java version markers - // Visit the compilation unit to find Java version markers - tree.getMarkers() - .findFirst(JavaVersion.class) - .ifPresent(version -> acc.javaVersion = Math.max(acc.javaVersion, version.getMajorVersion())); - } - return super.visit(tree, ctx); - } - }; - } - - @Override - public Collection generate(Accumulator acc, ExecutionContext ctx) { - if (!acc.sdkmanrcExists && 8 <= acc.javaVersion) { - // Create an empty .sdkmanrc file for now, and add content in getVisitor(Accumulator acc) - return singletonList( - PlainText.builder() - .text("") - .sourcePath(Paths.get(".sdkmanrc")) - .build() - ); - } - // If a .sdkmanrc file already exists, we will not generate a new one - return emptyList(); - } - - @Override - public TreeVisitor getVisitor(Accumulator acc) { - return Preconditions.check( - new FindSourceFiles(".sdkmanrc"), - new PlainTextVisitor() { - @Override - public PlainText visitText(PlainText text, ExecutionContext ctx) { - // No change needed if no java version is set or already matches - if (acc.javaVersion < 8 || text.getText().contains("java=" + acc.javaVersion)) { - return text; - } - // If the file already contains the highest java version, we do nothing - if (text.getText().contains("java=")) { - // If the file contains a java version but not the highest one, we will update it - return text.withText(text.getText().replaceAll("java=\\d+", "java=" + acc.javaVersion)); - } - // If the file does not contain a java version, we will add it - return text.withText(text.getText() + "\njava=" + acc.javaVersion); - } - } - ); - } -} diff --git a/src/main/java/com/yourorg/package-info.java b/src/main/java/com/yourorg/package-info.java index 27ab1b5..ae4fc41 100644 --- a/src/main/java/com/yourorg/package-info.java +++ b/src/main/java/com/yourorg/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 the original author or authors. + * 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. @@ -13,9 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -@NullMarked -@NonNullFields -package com.yourorg; - -import org.jspecify.annotations.NullMarked; -import org.openrewrite.internal.lang.NonNullFields; +/** + * Minecraft version migration recipes. + */ +package com.yourorg; \ No newline at end of file diff --git a/src/main/java/com/yourorg/table/ClassHierarchyReport.java b/src/main/java/com/yourorg/table/ClassHierarchyReport.java deleted file mode 100644 index 9be0c34..0000000 --- a/src/main/java/com/yourorg/table/ClassHierarchyReport.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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 com.yourorg.table; - -import lombok.Value; -import org.openrewrite.Column; -import org.openrewrite.DataTable; -import org.openrewrite.Recipe; - -public class ClassHierarchyReport extends DataTable { - - public ClassHierarchyReport(Recipe recipe) { - super(recipe, - "Class hierarchy report", - "Records inheritance relationships between classes."); - } - - @Value - public static class Row { - @Column(displayName = "Class name", - description = "Fully qualified name of the class.") - String className; - - @Column(displayName = "Relationship", - description = "Whether the class implements a super interface or extends a superclass.") - Relationship relationship; - - @Column(displayName = "Super class name", - description = "Fully qualified name of the superclass.") - String superClassName; - } - - public enum Relationship { - EXTENDS, - IMPLEMENTS - } -} diff --git a/src/main/java/com/yourorg/table/SpringBeansReport.java b/src/main/java/com/yourorg/table/SpringBeansReport.java deleted file mode 100644 index b25e657..0000000 --- a/src/main/java/com/yourorg/table/SpringBeansReport.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2025 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 com.yourorg.table; - -import com.fasterxml.jackson.annotation.JsonIgnoreType; -import lombok.Value; -import org.openrewrite.Column; -import org.openrewrite.DataTable; -import org.openrewrite.Recipe; - -@JsonIgnoreType -public class SpringBeansReport extends DataTable { - - public SpringBeansReport(Recipe recipe) { - super(recipe, "Spring bean definitions", - "Classes defined with a form of a Spring `@Bean` stereotype"); - } - - @Value - public static class Row { - @Column(displayName = "Source path", - description = "The path to the source file containing the component definition.") - String sourcePath; - - @Column(displayName = "Component name", - description = "The name of the component.") - String name; - } -} diff --git a/src/main/java/com/yourorg/table/TodoCommentsReport.java b/src/main/java/com/yourorg/table/TodoCommentsReport.java deleted file mode 100644 index c431193..0000000 --- a/src/main/java/com/yourorg/table/TodoCommentsReport.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.yourorg.table; - -import lombok.Value; -import org.openrewrite.Column; -import org.openrewrite.DataTable; -import org.openrewrite.Recipe; - -public class TodoCommentsReport extends DataTable { - - public TodoCommentsReport(Recipe recipe) { - super(recipe, - "Todo comments report", - "Records Todo comments and the type of element they are attached to."); - } - - @Value - public static class Row { - @Column(displayName = "Source path", - description = "The path to the source file containing the comment.") - String sourcePath; - - @Column(displayName = "Comment text", - description = "The text of the comment.") - String commentText; - - @Column(displayName = "Element type", - description = "The class and element type that the comment is attached to.") - String elementType; - } -} diff --git a/src/main/java/com/yourorg/trait/TodoComment.java b/src/main/java/com/yourorg/trait/TodoComment.java deleted file mode 100644 index d4da4c5..0000000 --- a/src/main/java/com/yourorg/trait/TodoComment.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.yourorg.trait; - -import lombok.Getter; -import lombok.Value; -import org.jspecify.annotations.Nullable; -import org.openrewrite.Cursor; -import org.openrewrite.Tree; -import org.openrewrite.java.tree.J; -import org.openrewrite.trait.SimpleTraitMatcher; -import org.openrewrite.trait.Trait; -import org.openrewrite.xml.tree.Xml; -import org.openrewrite.yaml.tree.Yaml; - -import java.util.ArrayList; -import java.util.List; - -// TODO - This is a placeholder for a trait. -// Implement a trait that that defines how to match `TODO` comments across Java, YAML, and XML files. -@Value -public class TodoComment implements Trait { - - @Getter - Cursor cursor; - - @Getter - List todos; - - public static class Matcher extends SimpleTraitMatcher { - @Override - protected @Nullable TodoComment test(Cursor cursor) { - Object v = cursor.getValue(); - if (!(v instanceof J) && !(v instanceof Yaml) && !(v instanceof Xml)) { - return null; - } - List todos = new ArrayList<>(); - if (v instanceof J) { - - } - else if (v instanceof Yaml) { - - } - else if (v instanceof Xml) { - - } - if (todos.isEmpty()) { - return null; - } - return new TodoComment(cursor, todos); - } - } -} diff --git a/src/test/java/com/yourorg/AppendToReleaseNotesTest.java b/src/test/java/com/yourorg/AppendToReleaseNotesTest.java deleted file mode 100644 index 53e250d..0000000 --- a/src/test/java/com/yourorg/AppendToReleaseNotesTest.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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 com.yourorg; - -import org.junit.jupiter.api.Test; -import org.openrewrite.DocumentExample; -import org.openrewrite.test.RecipeSpec; -import org.openrewrite.test.RewriteTest; - -import java.nio.file.Path; - -import static org.openrewrite.test.SourceSpecs.text; - -class AppendToReleaseNotesTest implements RewriteTest { - @Override - public void defaults(RecipeSpec spec) { - spec.recipe(new AppendToReleaseNotes("Hello world")); - } - - @DocumentExample - @Test - void editExistingReleaseNotes() { - // When the file does already exist, we assert the content is modified as expected. - rewriteRun( - text( - """ - You say goodbye, I say - """, - """ - You say goodbye, I say - Hello world - """, - spec -> spec.path(Path.of("RELEASE.md") - ) - ) - ); - } - - @Test - void createNewReleaseNotes() { - // Notice how the before text is doesNotExist(), indicating that the file does not exist yet. - // The after text is the content of the file after the recipe is applied. - rewriteRun( - text( - doesNotExist(), - """ - Hello world - """, - spec -> spec.path(Path.of("RELEASE.md") - ) - ) - ); - } -} diff --git a/src/test/java/com/yourorg/AssertEqualsToAssertThatTest.java b/src/test/java/com/yourorg/AssertEqualsToAssertThatTest.java deleted file mode 100644 index 76f8b58..0000000 --- a/src/test/java/com/yourorg/AssertEqualsToAssertThatTest.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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 com.yourorg; - -import org.junit.jupiter.api.Test; -import org.openrewrite.DocumentExample; -import org.openrewrite.java.JavaParser; -import org.openrewrite.test.RecipeSpec; -import org.openrewrite.test.RewriteTest; - -import static org.openrewrite.java.Assertions.java; - -class AssertEqualsToAssertThatTest implements RewriteTest { - - @Override - public void defaults(RecipeSpec spec) { - spec.recipe(new AssertEqualsToAssertThat()) - .parser(JavaParser.fromJavaVersion() - .classpath("junit-jupiter-api")); - } - - @DocumentExample - @Test - void twoArgument() { - rewriteRun( - //language=java - java( - """ - import org.junit.jupiter.api.Assertions; - - class A { - void foo() { - Assertions.assertEquals(1, 2); - } - } - """, - """ - import org.assertj.core.api.Assertions; - - class A { - void foo() { - Assertions.assertThat(2).isEqualTo(1); - } - } - """ - ) - ); - } - - @Test - void withDescription() { - rewriteRun( - //language=java - java( - """ - import org.junit.jupiter.api.Assertions; - - class A { - void foo() { - Assertions.assertEquals(1, 2, "one equals two, everyone knows that"); - } - } - """, - """ - import org.assertj.core.api.Assertions; - - class A { - void foo() { - Assertions.assertThat(2).as("one equals two, everyone knows that").isEqualTo(1); - } - } - """ - ) - ); - } -} diff --git a/src/test/java/com/yourorg/ClassHierarchyTest.java b/src/test/java/com/yourorg/ClassHierarchyTest.java deleted file mode 100644 index 3c8a306..0000000 --- a/src/test/java/com/yourorg/ClassHierarchyTest.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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 com.yourorg; - -import com.yourorg.table.ClassHierarchyReport; -import org.junit.jupiter.api.Test; -import org.openrewrite.test.RecipeSpec; -import org.openrewrite.test.RewriteTest; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.openrewrite.java.Assertions.java; - -class ClassHierarchyTest implements RewriteTest { - - @Override - public void defaults(RecipeSpec spec) { - spec.recipe(new ClassHierarchy()); - } - - @Test - void basic() { - rewriteRun( - spec -> spec.dataTable(ClassHierarchyReport.Row.class, rows -> { - assertThat(rows).containsExactly(new ClassHierarchyReport.Row("A", ClassHierarchyReport.Relationship.EXTENDS, "java.lang.Object")); - }), - //language=java - java( - """ - class A {} - """ - ) - ); - } - - @Test - void bExtendsA() { - rewriteRun( - spec -> spec.dataTable(ClassHierarchyReport.Row.class, rows -> { - assertThat(rows).containsExactly( - new ClassHierarchyReport.Row("A", ClassHierarchyReport.Relationship.EXTENDS, "java.lang.Object"), - new ClassHierarchyReport.Row("B", ClassHierarchyReport.Relationship.EXTENDS, "A")); - }), - //language=java - java( - """ - class A {} - """ - ), - //language=java - java( - """ - class B extends A {} - """ - ) - ); - } - - @Test - void interfaceRelationship() { - rewriteRun( - spec -> spec.dataTable(ClassHierarchyReport.Row.class, rows -> { - assertThat(rows).containsExactly( - new ClassHierarchyReport.Row("A", ClassHierarchyReport.Relationship.EXTENDS, "java.lang.Object"), - new ClassHierarchyReport.Row("A", ClassHierarchyReport.Relationship.IMPLEMENTS, "java.io.Serializable")); - }), - // language=java - java( - """ - import java.io.Serializable; - class A implements Serializable {} - """ - ) - ); - } -} diff --git a/src/test/java/com/yourorg/EqualsAvoidsNullTest.java b/src/test/java/com/yourorg/EqualsAvoidsNullTest.java deleted file mode 100644 index 64cd9f6..0000000 --- a/src/test/java/com/yourorg/EqualsAvoidsNullTest.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2025 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 com.yourorg; - -import org.junit.jupiter.api.Test; -import org.openrewrite.DocumentExample; -import org.openrewrite.test.RecipeSpec; -import org.openrewrite.test.RewriteTest; - -import static org.openrewrite.java.Assertions.java; - -class EqualsAvoidsNullTest implements RewriteTest { - @Override - public void defaults(RecipeSpec spec) { - spec.recipe(new EqualsAvoidsNullRecipe()); - } - - @DocumentExample - @Test - void beforeEquals() { - rewriteRun( - java( - """ - class Foo { - boolean unsafe(String actual) { - return actual.equals("literal"); - } - - boolean nullsafe(String actual) { - return actual != null && actual.equals("literal"); - } - } - """, - """ - class Foo { - boolean unsafe(String actual) { - return "literal".equals(actual); - } - - boolean nullsafe(String actual) { - return "literal".equals(actual); - } - } - """ - ) - ); - } - - @Test - void unchanged() { - rewriteRun( - java( - """ - class Foo { - String field = "field"; - - void unchanged(String actual, String other) { - // Already safe - boolean a = "literal".equals(actual); - - // Odd, but not changed - boolean b = "other".equals("literal"); - - // Not safe to change, as right side is not a literal, and could be null - boolean c = actual.equals(field); - boolean d = actual.equals(method()); - boolean e = actual.equals(other); - - // Null checked variants also not changed - boolean f = actual != null && actual.equals(field); - boolean g = actual != null && actual.equals(method()); - boolean h = actual != null && actual.equals(other); - } - - String method() { - return "method"; - } - } - """ - ) - ); - } -} diff --git a/src/test/java/com/yourorg/FindSpringBeansTest.java b/src/test/java/com/yourorg/FindSpringBeansTest.java deleted file mode 100644 index 3270e8c..0000000 --- a/src/test/java/com/yourorg/FindSpringBeansTest.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2025 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 com.yourorg; - -import com.yourorg.table.SpringBeansReport; -import org.junit.jupiter.api.Test; -import org.openrewrite.DocumentExample; -import org.openrewrite.PathUtils; -import org.openrewrite.java.JavaParser; -import org.openrewrite.test.RecipeSpec; -import org.openrewrite.test.RewriteTest; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.openrewrite.java.Assertions.java; - -class FindSpringBeansTest implements RewriteTest { - - // Note, you can define defaults for the RecipeSpec and these defaults will be used for all tests. - // In this case, the recipe and the parser are common. - @Override - public void defaults(RecipeSpec spec) { - // Note how we directly instantiate the recipe class here - spec.recipe(new FindSpringBeans()) - // The before examples are using Spring classes/annotations, so we need to add spring-context to the classpath - .parser(JavaParser.fromJavaVersion().classpath("spring-context")); - } - - @DocumentExample - @Test - void findSpringBeans() { - String filePath = PathUtils.separatorsToSystem("src/main/java/com/yourorg/MyConfig.java"); - rewriteRun( - spec -> spec.dataTable(SpringBeansReport.Row.class, rows -> - assertThat(rows) - .containsExactly( - new SpringBeansReport.Row(filePath, "bean"), - new SpringBeansReport.Row(filePath, "namedBean"), - new SpringBeansReport.Row(filePath, "useMethodNameWhenNoValuePresent") - )), - java( - """ - import org.springframework.context.annotation.Bean; - import org.springframework.context.annotation.Configuration; - - @Configuration - public class MyConfig { - @Bean("bean") - public String doNotUseMethodNameWhenDefaultIsPresent() { - return "Named Bean"; - } - - @Bean(name = "namedBean") - public String doNotUseMethodNameWhenNameIsPresent() { - return "Named Bean"; - } - - @Bean - public String useMethodNameWhenNoValuePresent() { - return "Named Bean"; - } - - @Override - public String doNotListOtherMethods() { - return "Private method"; - } - } - """, - """ - import org.springframework.context.annotation.Bean; - import org.springframework.context.annotation.Configuration; - - @Configuration - public class MyConfig { - /*~~(bean)~~>*/@Bean("bean") - public String doNotUseMethodNameWhenDefaultIsPresent() { - return "Named Bean"; - } - - /*~~(namedBean)~~>*/@Bean(name = "namedBean") - public String doNotUseMethodNameWhenNameIsPresent() { - return "Named Bean"; - } - - /*~~(useMethodNameWhenNoValuePresent)~~>*/@Bean - public String useMethodNameWhenNoValuePresent() { - return "Named Bean"; - } - - @Override - public String doNotListOtherMethods() { - return "Private method"; - } - } - """, - spec -> spec.path(filePath) - ) - ); - } -} diff --git a/src/test/java/com/yourorg/MinecraftVersionMigrationTest.java b/src/test/java/com/yourorg/MinecraftVersionMigrationTest.java new file mode 100644 index 0000000..c899766 --- /dev/null +++ b/src/test/java/com/yourorg/MinecraftVersionMigrationTest.java @@ -0,0 +1,179 @@ +/* + * 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 com.yourorg; + +import org.junit.jupiter.api.Test; +import org.openrewrite.DocumentExample; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +import java.nio.file.Path; + +import static org.openrewrite.gradle.Assertions.buildGradle; +import static org.openrewrite.properties.Assertions.properties; + +class MinecraftVersionMigrationTest implements RewriteTest { + + @Override + public void defaults(RecipeSpec spec) { + spec.recipe(new MinecraftVersionMigration("1.20.1", "1.21")); + } + + @DocumentExample + @Test + void updateMinecraftVersionInBuildGradle() { + rewriteRun( + buildGradle( + """ + plugins { + id 'java' + id 'net.neoforged.gradle' version '7.0.80' + } + + group 'com.example.mod' + version '1.0.0' + + repositories { + mavenCentral() + } + + dependencies { + implementation 'net.neoforged:neoforge:1.20.1-47.1.0' + implementation 'net.minecraft:server:1.20.1' + } + """, + """ + plugins { + id 'java' + id 'net.neoforged.gradle' version '7.0.80' + } + + group 'com.example.mod' + version '1.0.0' + + repositories { + mavenCentral() + } + + dependencies { + implementation 'net.neoforged:neoforge:1.21-47.1.0' + implementation 'net.minecraft:server:1.21' + } + """ + ) + ); + } + + @Test + void updateMinecraftVersionInGradleProperties() { + rewriteRun( + properties( + """ + minecraft_version=1.20.1 + forge_version=47.1.0 + neoforge_version=1.20.1-47.1.0 + mod_version=1.0.0 + """, + """ + minecraft_version=1.21 + forge_version=47.1.0 + neoforge_version=1.21-47.1.0 + mod_version=1.0.0 + """, + spec -> spec.path(Path.of("gradle.properties")) + ) + ); + } + + @Test + void updateMultipleVersionsInBuildGradle() { + rewriteRun( + buildGradle( + """ + plugins { + id 'java' + id 'net.minecraftforge.gradle' version '5.1.+' + } + + minecraft { + version = '1.20.1' + mappings = 'official_1.20.1' + } + + dependencies { + minecraft 'net.minecraftforge:forge:1.20.1-47.1.0' + implementation 'net.minecraft:client:1.20.1' + } + """, + """ + plugins { + id 'java' + id 'net.minecraftforge.gradle' version '5.1.+' + } + + minecraft { + version = '1.21' + mappings = 'official_1.21' + } + + dependencies { + minecraft 'net.minecraftforge:forge:1.21-47.1.0' + implementation 'net.minecraft:client:1.21' + } + """ + ) + ); + } + + @Test + void noChangeWhenVersionNotFound() { + rewriteRun( + buildGradle( + """ + plugins { + id 'java' + } + + dependencies { + implementation 'com.example:lib:1.0.0' + } + """ + ) + ); + } + + @Test + void updateFabricVersionInGradleProperties() { + rewriteRun( + spec -> spec.recipe(new MinecraftVersionMigration("1.19.2", "1.20.1")), + properties( + """ + # Fabric Properties + minecraft_version=1.19.2 + fabric_loader_version=0.14.9 + fabric_version=0.67.0+1.19.2 + """, + """ + # Fabric Properties + minecraft_version=1.20.1 + fabric_loader_version=0.14.9 + fabric_version=0.67.0+1.20.1 + """, + spec -> spec.path(Path.of("gradle.properties")) + ) + ); + } +} \ No newline at end of file diff --git a/src/test/java/com/yourorg/NoCollectionMutationTest.java b/src/test/java/com/yourorg/NoCollectionMutationTest.java deleted file mode 100644 index 69c8fbd..0000000 --- a/src/test/java/com/yourorg/NoCollectionMutationTest.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * 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 com.yourorg; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.openrewrite.DocumentExample; -import org.openrewrite.java.JavaParser; -import org.openrewrite.test.RecipeSpec; -import org.openrewrite.test.RewriteTest; - -import static org.openrewrite.java.Assertions.java; - - -@SuppressWarnings({"NullableProblems", "WriteOnlyObject", "ResultOfMethodCallIgnored", "DataFlowIssue"}) -class NoCollectionMutationTest implements RewriteTest { - - @Override - public void defaults(RecipeSpec spec) { - spec.recipe(new NoCollectionMutation()).parser(JavaParser.fromJavaVersion().classpath("rewrite-core", "rewrite-java")); - } - - @DocumentExample - @Test - void inlineMutation() { - rewriteRun( - //language=java - java( - """ - import org.openrewrite.ExecutionContext; - import org.openrewrite.java.JavaIsoVisitor; - import org.openrewrite.java.tree.J; - - public class ManipulateMethodArguments extends JavaIsoVisitor { - @Override - public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { - method.getArguments().clear(); - return method; - } - } - """, - """ - import org.openrewrite.ExecutionContext; - import org.openrewrite.java.JavaIsoVisitor; - import org.openrewrite.java.tree.J; - - import java.util.ArrayList; - - public class ManipulateMethodArguments extends JavaIsoVisitor { - @Override - public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { - new ArrayList<>(method.getArguments()).clear(); - return method; - } - } - """ - ) - ); - } - - @Test - void nonMutationIsOkay() { - rewriteRun( - //language=java - java( - """ - import org.openrewrite.ExecutionContext; - import org.openrewrite.java.JavaIsoVisitor; - import org.openrewrite.java.tree.J; - import org.openrewrite.internal.ListUtils; - - public class ManipulateMethodArguments extends JavaIsoVisitor { - @Override - public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { - method.getArguments().isEmpty(); - method.getSideEffects().indexOf(null); - method.getTypeParameters().toArray(); - return method; - } - } - """ - ) - ); - } - - @Test - void subsequentMutation() { - rewriteRun( - //language=java - java( - """ - import org.openrewrite.ExecutionContext; - import org.openrewrite.java.JavaIsoVisitor; - import org.openrewrite.java.tree.Expression; - import org.openrewrite.java.tree.J; - - import java.util.List; - - public class ManipulateMethodArguments extends JavaIsoVisitor { - @Override - public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { - List args = method.getArguments(); - if(!args.isEmpty()) { - args.remove(0); - } - return method; - } - } - """, - """ - import org.openrewrite.ExecutionContext; - import org.openrewrite.java.JavaIsoVisitor; - import org.openrewrite.java.tree.Expression; - import org.openrewrite.java.tree.J; - - import java.util.ArrayList; - import java.util.List; - - public class ManipulateMethodArguments extends JavaIsoVisitor { - @Override - public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { - List args = new ArrayList<>(method.getArguments()); - if(!args.isEmpty()) { - args.remove(0); - } - return method; - } - } - """ - ) - ); - } - - @Disabled("Local dataflow is not capable of following what happens to a variable passed into a function") - @Test - void mutationInFunction() { - rewriteRun( - //language=java - java( - """ - import org.openrewrite.ExecutionContext; - import org.openrewrite.java.JavaIsoVisitor; - import org.openrewrite.java.tree.Expression; - import org.openrewrite.java.tree.J; - - import java.util.List; - - public class ManipulateMethodArguments extends JavaIsoVisitor { - @Override - public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { - List a = method.getArguments(); - removeFirst(a); - return method; - } - - private void removeFirst(List args) { - if(!args.isEmpty()) { - args.remove(0); - } - } - } - """, - """ - import org.openrewrite.ExecutionContext; - import org.openrewrite.java.JavaIsoVisitor; - import org.openrewrite.java.tree.Expression; - import org.openrewrite.java.tree.J; - - import java.util.ArrayList; - import java.util.List; - - public class ManipulateMethodArguments extends JavaIsoVisitor { - @Override - public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { - List a = new ArrayList<>(method.getArguments()); - removeFirst(a); - return method; - } - - private void removeFirst(List args) { - if(!args.isEmpty()) { - args.remove(0); - } - } - } - """ - ) - ); - } - - @Test - void listUtilsIsOkay() { - rewriteRun( - //language=java - java( - """ - import org.openrewrite.ExecutionContext; - import org.openrewrite.java.JavaIsoVisitor; - import org.openrewrite.java.tree.J; - import org.openrewrite.internal.ListUtils; - - public class ManipulateMethodArguments extends JavaIsoVisitor { - @Override - public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { - ListUtils.map(method.getArguments(), it -> it).clear(); - return method; - } - } - """ - ) - ); - } -} diff --git a/src/test/java/com/yourorg/NoGuavaListsNewArrayListTest.java b/src/test/java/com/yourorg/NoGuavaListsNewArrayListTest.java deleted file mode 100644 index 0d2d229..0000000 --- a/src/test/java/com/yourorg/NoGuavaListsNewArrayListTest.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright 2021 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 com.yourorg; - -import org.junit.jupiter.api.Test; -import org.openrewrite.DocumentExample; -import org.openrewrite.java.JavaParser; -import org.openrewrite.test.RecipeSpec; -import org.openrewrite.test.RewriteTest; - -import static org.openrewrite.java.Assertions.java; - -// This is a test for the NoGuavaListsNewArrayList recipe, as an example of how to write a test for an imperative recipe. -class NoGuavaListsNewArrayListTest implements RewriteTest { - - // Note, you can define defaults for the RecipeSpec and these defaults will be used for all tests. - // In this case, the recipe and the parser are common. See below, on how the defaults can be overridden - // per test. - @Override - public void defaults(RecipeSpec spec) { - // Note how we directly instantiate the recipe class here - spec.recipe(new NoGuavaListsNewArrayList()) - .parser(JavaParser.fromJavaVersion() - .logCompilationWarningsAndErrors(true) - // The before/after examples are using Guava classes, so we need to add the Guava library to the classpath - .classpath("guava")); - } - - @DocumentExample - @Test - void replaceWithNewArrayList() { - rewriteRun( - // There is an overloaded version or rewriteRun that allows the RecipeSpec to be customized specifically - // for a given test. In this case, the parser for this test is configured to not log compilation warnings. - spec -> spec - .parser(JavaParser.fromJavaVersion() - .logCompilationWarningsAndErrors(false) - .classpath("guava")), - // language=java - java( - """ - import com.google.common.collect.*; - - import java.util.List; - - class Test { - List cardinalsWorldSeries = Lists.newArrayList(); - } - """, - """ - import java.util.ArrayList; - import java.util.List; - - class Test { - List cardinalsWorldSeries = new ArrayList<>(); - } - """ - ) - ); - } - - @Test - void replaceWithNewArrayListIterable() { - rewriteRun( - // language=java - java( - """ - import com.google.common.collect.*; - - import java.util.Collections; - import java.util.List; - - class Test { - List l = Collections.emptyList(); - List cardinalsWorldSeries = Lists.newArrayList(l); - } - """, - """ - import java.util.ArrayList; - import java.util.Collections; - import java.util.List; - - class Test { - List l = Collections.emptyList(); - List cardinalsWorldSeries = new ArrayList<>(l); - } - """ - ) - ); - } - - @Test - void replaceWithNewArrayListWithCapacity() { - rewriteRun( - // language=java - java( - """ - import com.google.common.collect.*; - - import java.util.ArrayList; - import java.util.List; - - class Test { - List cardinalsWorldSeries = Lists.newArrayListWithCapacity(2); - } - """, - """ - import java.util.ArrayList; - import java.util.List; - - class Test { - List cardinalsWorldSeries = new ArrayList<>(2); - } - """ - ) - ); - } - - // This test is to show that the `super.visitMethodInvocation` is needed to ensure that nested method invocations are visited. - @Test - void showNeedForSuperVisitMethodInvocation() { - rewriteRun( - //language=java - java( - """ - import com.google.common.collect.*; - - import java.util.Collections; - import java.util.List; - - class Test { - List cardinalsWorldSeries = Collections.unmodifiableList(Lists.newArrayList()); - } - """, - """ - import java.util.ArrayList; - import java.util.Collections; - import java.util.List; - - class Test { - List cardinalsWorldSeries = Collections.unmodifiableList(new ArrayList<>()); - } - """ - ) - ); - } - - // Often you want to make sure no changes are made when the target state is already achieved. - // To do so only pass in a before state and no after state to the rewriteRun method SourceSpecs. - @Test - void noChangeNecessary() { - rewriteRun( - //language=java - java( - """ - import java.util.ArrayList; - import java.util.Collections; - import java.util.List; - - class Test { - List cardinalsWorldSeries = Collections.unmodifiableList(new ArrayList<>()); - } - """ - ) - ); - } -} diff --git a/src/test/java/com/yourorg/SimplifyTernaryTest.java b/src/test/java/com/yourorg/SimplifyTernaryTest.java deleted file mode 100644 index 36d6b2a..0000000 --- a/src/test/java/com/yourorg/SimplifyTernaryTest.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * 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 com.yourorg; - -import org.junit.jupiter.api.Test; -import org.openrewrite.DocumentExample; -import org.openrewrite.test.RecipeSpec; -import org.openrewrite.test.RewriteTest; - -import static org.openrewrite.java.Assertions.java; - -// This is a test for the SimplifyTernary recipe, as an example of how to write a test for a Refaster style recipe. -class SimplifyTernaryTest implements RewriteTest { - - @Override - public void defaults(RecipeSpec spec) { - // Note that we instantiate a generated class here, with `Recipes` appended to the Refaster class name - spec.recipe(new SimplifyTernaryRecipes()); - } - - @DocumentExample - @Test - void simplified() { - rewriteRun( - //language=java - java( - """ - class Test { - boolean trueCondition1 = true ? true : false; - boolean trueCondition2 = false ? false : true; - boolean trueCondition3 = booleanExpression() ? true : false; - boolean trueCondition4 = trueCondition1 && trueCondition2 ? true : false; - boolean trueCondition5 = !true ? false : true; - boolean trueCondition6 = !false ? true : false; - - boolean falseCondition1 = true ? false : true; - boolean falseCondition2 = !false ? false : true; - boolean falseCondition3 = booleanExpression() ? false : true; - boolean falseCondition4 = trueCondition1 && trueCondition2 ? false : true; - boolean falseCondition5 = !false ? false : true; - boolean falseCondition6 = !true ? true : false; - - boolean binary1 = booleanExpression() && booleanExpression() ? true : false; - boolean binary2 = booleanExpression() && booleanExpression() ? false : true; - boolean binary3 = booleanExpression() || booleanExpression() ? true : false; - boolean binary4 = booleanExpression() || booleanExpression() ? false : true; - - boolean booleanExpression() { - return true; - } - } - """, - """ - class Test { - boolean trueCondition1 = true; - boolean trueCondition2 = true; - boolean trueCondition3 = booleanExpression(); - boolean trueCondition4 = trueCondition1 && trueCondition2; - boolean trueCondition5 = true; - boolean trueCondition6 = true; - - boolean falseCondition1 = false; - boolean falseCondition2 = false; - boolean falseCondition3 = !booleanExpression(); - boolean falseCondition4 = !(trueCondition1 && trueCondition2); - boolean falseCondition5 = false; - boolean falseCondition6 = false; - - boolean binary1 = booleanExpression() && booleanExpression(); - boolean binary2 = !(booleanExpression() && booleanExpression()); - boolean binary3 = booleanExpression() || booleanExpression(); - boolean binary4 = !(booleanExpression() || booleanExpression()); - - boolean booleanExpression() { - return true; - } - } - """ - ) - ); - } - - // It's good practice to also include a test that verifies that the recipe doesn't change anything when it shouldn't. - @Test - void unchanged() { - rewriteRun( - //language=java - java( - """ - class Test { - boolean unchanged1 = booleanExpression() ? booleanExpression() : !booleanExpression(); - boolean unchanged2 = booleanExpression() ? true : !booleanExpression(); - boolean unchanged3 = booleanExpression() ? booleanExpression() : false; - - boolean booleanExpression() { - return true; - } - } - """ - ) - ); - } -} diff --git a/src/test/java/com/yourorg/StringIsEmptyTest.java b/src/test/java/com/yourorg/StringIsEmptyTest.java deleted file mode 100644 index 57b759f..0000000 --- a/src/test/java/com/yourorg/StringIsEmptyTest.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * 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 com.yourorg; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.openrewrite.DocumentExample; -import org.openrewrite.Recipe; -import org.openrewrite.test.RecipeSpec; -import org.openrewrite.test.RewriteTest; - -import static org.openrewrite.java.Assertions.java; - -@Disabled("Remove this annotation to run the tests once you implement the recipe") -class StringIsEmptyTest implements RewriteTest { - - @Override - public void defaults(RecipeSpec spec) { - // Note that we instantiate a generated class here, with `Recipes` appended to the Refaster class name - // You might need to trigger an explicit build of your project to generate this class with Ctrl + F9 - - // TODO: Uncomment the line below once you have implemented the recipe - //spec.recipe(new StringIsEmptyRecipe()); - } - - @DocumentExample - @Test - void standardizeStringIsEmpty() { - // Notice how we pass in both the "before" and "after" code snippets - // This indicates that we expect the recipe to transform the "before" code snippet into the "after" code snippet - // If the recipe does not do this, the test will fail, and a diff will be shown - rewriteRun( - //language=java - java( - """ - class A { - void test(String s, boolean b) { - b = s.length() == 0; - b = 0 == s.length(); - b = s.length() < 1; - b = 1 > s.length(); - b = s.equals(""); - b = "".equals(s); - b = s.isEmpty(); - } - } - """, - """ - class A { - void test(String s, boolean b) { - b = s.isEmpty(); - b = s.isEmpty(); - b = s.isEmpty(); - b = s.isEmpty(); - b = s.isEmpty(); - b = s.isEmpty(); - b = s.isEmpty(); - } - } - """ - ) - ); - } - - @Test - void showStringTypeMatchAndSimplification() { - // Notice how the recipe will match anything that is of type String, not just local variables - // Take a closer look at the last two replacements to `true` and `false`. - // Open up the generated recipe and see if you can work out why those are replaced with booleans! - rewriteRun( - //language=java - java( - """ - class A { - String field; - - String methodCall() { - return "Hello World"; - } - - void test(String argument) { - boolean bool1 = field.length() == 0; - boolean bool2 = methodCall().length() == 0; - boolean bool3 = argument.length() == 0; - boolean bool4 = "".length() == 0; - boolean bool5 = "literal".length() == 0; - } - } - """, - """ - class A { - String field; - - String methodCall() { - return "Hello World"; - } - - void test(String argument) { - boolean bool1 = field.isEmpty(); - boolean bool2 = methodCall().isEmpty(); - boolean bool3 = argument.isEmpty(); - boolean bool4 = true; - boolean bool5 = false; - } - } - """ - ) - ); - } - - @Test - void doNothingForStringIsEmpty() { - // Notice how we only pass in the "before" code snippet, and not the "after" code snippet - // That indicates that we expect the recipe to do nothing in this case, and will fail if it does anything - rewriteRun( - //language=java - java( - """ - class A { - void test(String s, boolean b) { - b = s.isEmpty(); - } - } - """ - ) - ); - } - - @Test - void doNothingForCharSequence() { - // When a different type is used, the recipe should do nothing - // See if you can modify the recipe to handle CharSequence as well, or create a separate recipe for it - rewriteRun( - //language=java - java( - """ - class A { - void test(CharSequence s, boolean b) { - b = s.length() == 0; - } - } - """ - ) - ); - } - - @Test - void recipeDocumentation() { - // This is a test to validate the correctness of the documentation in the recipe - // By default you get generated documentation, but you can customize it through the RecipeDescriptor annotation - Recipe recipe = null; // TODO: = new StringIsEmptyRecipe(); - String displayName = recipe.getDisplayName(); - String description = recipe.getDescription(); - assert "Standardize empty String checks".equals(displayName) : displayName; - assert "Replace calls to `String.length() == 0` with `String.isEmpty()`.".equals(description) : description; - } -} diff --git a/src/test/java/com/yourorg/TrackJavaTodosTest.java b/src/test/java/com/yourorg/TrackJavaTodosTest.java deleted file mode 100644 index ee22ea8..0000000 --- a/src/test/java/com/yourorg/TrackJavaTodosTest.java +++ /dev/null @@ -1,120 +0,0 @@ -package com.yourorg; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.openrewrite.DocumentExample; -import org.openrewrite.test.RecipeSpec; -import org.openrewrite.test.RewriteTest; - -import java.nio.file.Path; - -import static org.openrewrite.java.Assertions.java; -import static org.openrewrite.test.SourceSpecs.text; - -class TrackJavaTodosTest implements RewriteTest { - @Override - public void defaults(RecipeSpec spec) { - spec.recipe(new TrackJavaTodos("## Test Header")); - } - - @Disabled - @DocumentExample - @Test - void createNewTodoFile() { - // Notice how the before text is doesNotExist(), indicating that the file does not exist yet. - // The after text is the content of the file after the recipe is applied. - rewriteRun( - //language=java - java( - """ - class A { - // TODO: Have fun - /* TODO: Test your code */ - // Just a regular comment - public String foo() { - // TODO: Learn - return "bar"; - } - // Another regular comment - } - """ - ), - //language=markdown - text( - doesNotExist(), - """ - ## Test Header - TODO: Have fun - TODO: Test your code - TODO: Learn - """, - spec -> spec.path(Path.of("TODO.md")).noTrim() - ) - ); - } - - @Disabled - @Test - void editExistingTodoFile() { - rewriteRun( - //language=java - java( - """ - class A { - // TODO: Have fun - /* TODO: Test your code */ - // Just a regular comment - public String foo() { - // TODO: Learn - return "bar"; - } - // Another regular comment - } - """ - ), - //language=markdown - text( - "", - """ - ## Test Header - TODO: Have fun - TODO: Test your code - TODO: Learn - """ + "\n", - spec -> spec.path(Path.of("TODO.md")).noTrim() - ) - ); - } - - @Disabled - @Test - void doNotTouchExistingCorrectFile() { - rewriteRun( - //language=java - java( - """ - class A { - // TODO: Have fun - /* TODO: Test your code */ - // Just a regular comment - public String foo() { - // TODO: Learn - return "bar"; - } - // Another regular comment - } - """ - ), - //language=markdown - text( - """ - ## Test Header - TODO: Have fun - TODO: Test your code - TODO: Learn - """, - spec -> spec.path(Path.of("TODO.md")).noTrim() - ) - ); - } -} diff --git a/src/test/java/com/yourorg/TrackTodosTest.java b/src/test/java/com/yourorg/TrackTodosTest.java deleted file mode 100644 index 48a4bb0..0000000 --- a/src/test/java/com/yourorg/TrackTodosTest.java +++ /dev/null @@ -1,268 +0,0 @@ -package com.yourorg; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.openrewrite.DocumentExample; -import org.openrewrite.test.RecipeSpec; -import org.openrewrite.test.RewriteTest; -import org.openrewrite.test.SourceSpec; - -import java.nio.file.Path; - -import static org.openrewrite.java.Assertions.java; -import static org.openrewrite.test.SourceSpecs.text; -import static org.openrewrite.xml.Assertions.xml; -import static org.openrewrite.yaml.Assertions.yaml; - -class TrackTodosTest implements RewriteTest { - @Override - public void defaults(RecipeSpec spec) { - spec.recipe(new TrackTodos("## Test Header")) - .allSources(SourceSpec::noTrim); - } - - @Disabled - @DocumentExample - @Test - void createNewTodoFileJava() { - // Notice how the before text is doesNotExist(), indicating that the file does not exist yet. - // The after text is the content of the file after the recipe is applied. - rewriteRun( - // TODO: Uncomment the lines below once you have implemented the recipe - //spec -> spec.dataTable(TodoCommentsReport.Row.class, rows -> { - // assertThat(rows).containsExactly( - // new TodoCommentsReport.Row("A.java", "TODO: Have fun", "class org.openrewrite.java.tree.J$MethodDeclaration"), - // new TodoCommentsReport.Row("A.java", "TODO: Test your code", "class org.openrewrite.java.tree.J$MethodDeclaration"), - // new TodoCommentsReport.Row("A.java", "TODO: Learn", "class org.openrewrite.java.tree.J$Return")); - //}), - //language=java - java( - """ - class A { - // TODO: Have fun - /* TODO: Test your code */ - // Just a regular comment - public String foo() { - // TODO: Learn - return "bar"; - } - // Another regular comment - } - """ - ), - //language=markdown - text( - doesNotExist(), - """ - ## Test Header - TODO: Have fun - TODO: Test your code - TODO: Learn - """, - spec -> spec.path(Path.of("TODO.md") - ) - ) - ); - } - - @Disabled - @Test - void editExistingTodoFileJava() { - rewriteRun( - // TODO: Uncomment the lines below once you have implemented the recipe - //spec -> spec.dataTable(TodoCommentsReport.Row.class, rows -> { - // assertThat(rows).containsExactly( - // new TodoCommentsReport.Row("A.java", "TODO: Have fun", "class org.openrewrite.java.tree.J$MethodDeclaration"), - // new TodoCommentsReport.Row("A.java", "TODO: Test your code", "class org.openrewrite.java.tree.J$MethodDeclaration"), - // new TodoCommentsReport.Row("A.java", "TODO: Learn", "class org.openrewrite.java.tree.J$Return")); - //}), - //language=java - java( - """ - class A { - // TODO: Have fun - /* TODO: Test your code */ - // Just a regular comment - public String foo() { - // TODO: Learn - return "bar"; - } - // Another regular comment - } - """ - ), - //language=markdown - text( - "", - """ - ## Test Header - TODO: Have fun - TODO: Test your code - TODO: Learn - """ + "\n", - spec -> spec.path(Path.of("TODO.md") - ) - ) - ); - } - - @Disabled - @Test - void createNewTodoFileYAML() { - // Notice how the before text is doesNotExist(), indicating that the file does not exist yet. - // The after text is the content of the file after the recipe is applied. - rewriteRun( - // TODO: Uncomment the lines below once you have implemented the recipe - //spec -> spec.dataTable(TodoCommentsReport.Row.class, rows -> { - // assertThat(rows).containsExactly( - // new TodoCommentsReport.Row("file.yaml", "TODO: Have fun", "class org.openrewrite.yaml.tree.Yaml$Document"), - // new TodoCommentsReport.Row("file.yaml", "TODO: Test your code", "class org.openrewrite.yaml.tree.Yaml$Mapping$Entry"), - // new TodoCommentsReport.Row("file.yaml", "TODO: Learn", "class org.openrewrite.yaml.tree.Yaml$Mapping$Entry")); - //}), - //language=yaml - yaml( - """ - # TODO: Have fun - someyaml: "here" - moreyaml: "there" - # TODO: Test your code - # TODO: Learn - # Just a regular comment - tabs: - are: "fun" - """ - ), - //language=markdown - text( - doesNotExist(), - """ - ## Test Header - TODO: Have fun - TODO: Test your code - TODO: Learn - """, - spec -> spec.path(Path.of("TODO.md") - ) - ) - ); - } - - @Disabled - @Test - void editExistingTodoFileYAML() { - rewriteRun( - // TODO: Uncomment the lines below once you have implemented the recipe - //spec -> spec.dataTable(TodoCommentsReport.Row.class, rows -> { - // assertThat(rows).containsExactly( - // new TodoCommentsReport.Row("file.yaml", "TODO: Have fun", "class org.openrewrite.yaml.tree.Yaml$Document"), - // new TodoCommentsReport.Row("file.yaml", "TODO: Test your code", "class org.openrewrite.yaml.tree.Yaml$Mapping$Entry"), - // new TodoCommentsReport.Row("file.yaml", "TODO: Learn", "class org.openrewrite.yaml.tree.Yaml$Mapping$Entry")); - //}), - //language=yaml - yaml( - """ - # TODO: Have fun - someyaml: "here" - moreyaml: "there" - # TODO: Test your code - # TODO: Learn - # Just a regular comment - tabs: - are: "fun" - """ - ), - //language=markdown - text( - "", - """ - ## Test Header - TODO: Have fun - TODO: Test your code - TODO: Learn - """ + "\n", - spec -> spec.path(Path.of("TODO.md") - ) - ) - ); - } - - @Disabled - @Test - void createNewTodoFileXML() { - // Notice how the before text is doesNotExist(), indicating that the file does not exist yet. - // The after text is the content of the file after the recipe is applied. - rewriteRun( - // TODO: Uncomment the lines below once you have implemented the recipe - //spec -> spec.dataTable(TodoCommentsReport.Row.class, rows -> { - // assertThat(rows).containsExactly( - // new TodoCommentsReport.Row("file.xml", "TODO: Have fun", "class org.openrewrite.xml.tree.Xml$Prolog"), - // new TodoCommentsReport.Row("file.xml", "TODO: Test your code", "class org.openrewrite.xml.tree.Xml$Prolog"), - // new TodoCommentsReport.Row("file.xml", "TODO: Learn", "class org.openrewrite.xml.tree.Xml$Tag")); - //}), - //language=xml - xml( - """ - - - - too - - for me - - - """ - ), - //language=markdown - text( - doesNotExist(), - """ - ## Test Header - TODO: Have fun - TODO: Test your code - TODO: Learn - """, - spec -> spec.path(Path.of("TODO.md") - ) - ) - ); - } - - @Disabled - @Test - void editExistingTodoFileXML() { - rewriteRun( - // TODO: Uncomment the lines below once you have implemented the recipe - //spec -> spec.dataTable(TodoCommentsReport.Row.class, rows -> { - // assertThat(rows).containsExactly( - // new TodoCommentsReport.Row("file.xml", "TODO: Have fun", "class org.openrewrite.xml.tree.Xml$Prolog"), - // new TodoCommentsReport.Row("file.xml", "TODO: Test your code", "class org.openrewrite.xml.tree.Xml$Prolog"), - // new TodoCommentsReport.Row("file.xml", "TODO: Learn", "class org.openrewrite.xml.tree.Xml$Tag")); - //}), - //language=xml - xml( - """ - - - - too - - for me - - - """ - ), - //language=markdown - text( - "", - """ - ## Test Header - TODO: Have fun - TODO: Test your code - TODO: Learn - """ + "\n", - spec -> spec.path(Path.of("TODO.md") - ) - ) - ); - } -} diff --git a/src/test/java/com/yourorg/UpdateConcoursePipelineTest.java b/src/test/java/com/yourorg/UpdateConcoursePipelineTest.java deleted file mode 100644 index 1aead51..0000000 --- a/src/test/java/com/yourorg/UpdateConcoursePipelineTest.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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 com.yourorg; - -import org.junit.jupiter.api.Test; -import org.openrewrite.DocumentExample; -import org.openrewrite.test.RewriteTest; - -import java.nio.file.Path; - -import static org.openrewrite.yaml.Assertions.yaml; - -class UpdateConcoursePipelineTest implements RewriteTest { - - @DocumentExample - @Test - void updateTagFilter() { - rewriteRun( - spec -> spec.recipe(new UpdateConcoursePipeline("8.2.0")), - //language=yaml - yaml( - """ - --- - resources: - - name: tasks - type: git - source: - uri: git@github.com:Example/concourse-tasks.git - tag_filter: 8.1.0 - """, - """ - --- - resources: - - name: tasks - type: git - source: - uri: git@github.com:Example/concourse-tasks.git - tag_filter: 8.2.0 - """, - spec -> spec.path(Path.of("ci/pipeline.yml")) - ) - ); - } -} diff --git a/src/test/java/com/yourorg/UseApacheStringUtilsTest.java b/src/test/java/com/yourorg/UseApacheStringUtilsTest.java deleted file mode 100644 index 599d705..0000000 --- a/src/test/java/com/yourorg/UseApacheStringUtilsTest.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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 com.yourorg; - -import org.junit.jupiter.api.Test; -import org.openrewrite.DocumentExample; -import org.openrewrite.java.JavaParser; -import org.openrewrite.test.RecipeSpec; -import org.openrewrite.test.RewriteTest; - -import static org.openrewrite.java.Assertions.java; - -class UseApacheStringUtilsTest implements RewriteTest { - @Override - public void defaults(RecipeSpec spec) { - spec.recipeFromResources("com.yourorg.UseApacheStringUtils") - // Notice how we only pass in `spring-core` as the classpath, but not `commons-lang3`. - // That's because we only need dependencies to compile the before code blocks, not the after code blocks. - .parser(JavaParser.fromJavaVersion().classpath("spring-core")); - } - - @DocumentExample - @Test - void replacesStringEquals() { - rewriteRun( - //language=java - java( - """ - import org.springframework.util.StringUtils; - - class A { - boolean test(String s) { - return StringUtils.containsWhitespace(s); - } - } - """, - """ - import org.apache.commons.lang3.StringUtils; - - class A { - boolean test(String s) { - return StringUtils.containsWhitespace(s); - } - } - """ - ) - ); - } - - @Test - void noChangeWhenAlreadyUsingCommonsLang3() { - rewriteRun( - // By passing in `commons-lang3` as the classpath here, we ensure that the before code block compiles. - spec -> spec.parser(JavaParser.fromJavaVersion().classpath("commons-lang3")), - //language=java - java( - """ - import org.apache.commons.lang3.StringUtils; - - class A { - boolean test(String s) { - return StringUtils.containsWhitespace(s); - } - } - """ - // The absence of a second argument to `java` indicates that the after code block should be the same as the before code block. - ) - ); - } -} diff --git a/src/test/java/com/yourorg/UseIntegerValueOfTest.java b/src/test/java/com/yourorg/UseIntegerValueOfTest.java deleted file mode 100644 index 26ad9e4..0000000 --- a/src/test/java/com/yourorg/UseIntegerValueOfTest.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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 com.yourorg; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.openrewrite.DocumentExample; -import org.openrewrite.test.RecipeSpec; -import org.openrewrite.test.RewriteTest; - -import static org.openrewrite.java.Assertions.java; - -@Disabled("Remove this annotation to run the tests once you implement the recipe") -class UseIntegerValueOfTest implements RewriteTest { - - @Override - public void defaults(RecipeSpec spec) { - spec.recipe(new UseIntegerValueOf()); - } - - @DocumentExample - @Test - void replacesNewIntegerWithValueOf() { - rewriteRun( - java( - """ - class Test { - Integer i = new Integer(42); - } - """, - """ - class Test { - Integer i = Integer.valueOf(42); - } - """ - ) - ); - } - - @Test - void replacesNewIntegerWithParseInt() { - rewriteRun( - java( - """ - class Test { - Integer i = new Integer("42"); - } - """, - """ - class Test { - Integer i = Integer.parseInt("42"); - } - """ - ) - ); - } -} diff --git a/src/test/java/com/yourorg/UseOpenRewriteNullableTest.java b/src/test/java/com/yourorg/UseOpenRewriteNullableTest.java deleted file mode 100644 index e1ca70d..0000000 --- a/src/test/java/com/yourorg/UseOpenRewriteNullableTest.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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 com.yourorg; - -import org.junit.jupiter.api.Test; -import org.openrewrite.DocumentExample; -import org.openrewrite.java.JavaParser; -import org.openrewrite.test.RecipeSpec; -import org.openrewrite.test.RewriteTest; - -import static org.openrewrite.java.Assertions.java; - -// This is a test for the UseOpenRewriteNullable recipe, as an example of how to write a test for a declarative recipe. -class UseOpenRewriteNullableTest implements RewriteTest { - @Override - public void defaults(RecipeSpec spec) { - spec - // Use the fully qualified class name of the recipe defined in src/main/resources/META-INF/rewrite/rewrite.yml - .recipeFromResources("com.yourorg.UseOpenRewriteNullable") - // The before and after text blocks contain references to annotations from these two classpath entries - .parser(JavaParser.fromJavaVersion().classpath("annotations", "rewrite-core")); - } - - @DocumentExample - @Test - void replacesNullableAnnotation() { - rewriteRun( - // Composite recipes are a hierarchy of recipes that can be applied in a single pass. - // To view what the composite recipe does, you can use the RecipePrinter to print the recipe to the console. - spec -> spec.printRecipe(() -> System.out::println), - //language=java - java( - """ - import org.jetbrains.annotations.Nullable; - - class A { - @Nullable - String s; - } - """, - """ - import org.openrewrite.internal.lang.Nullable; - - class A { - @Nullable - String s; - } - """ - ) - ); - } -} diff --git a/src/test/java/com/yourorg/UseSdkManJavaVersionTest.java b/src/test/java/com/yourorg/UseSdkManJavaVersionTest.java deleted file mode 100644 index f355347..0000000 --- a/src/test/java/com/yourorg/UseSdkManJavaVersionTest.java +++ /dev/null @@ -1,253 +0,0 @@ -package com.yourorg; - -import org.junit.jupiter.api.Test; -import org.openrewrite.DocumentExample; -import org.openrewrite.test.RecipeSpec; -import org.openrewrite.test.RewriteTest; - -import java.nio.file.Path; - -import static org.openrewrite.java.Assertions.*; -import static org.openrewrite.test.SourceSpecs.text; - -class UseSdkManJavaVersionTest implements RewriteTest { - - @Override - public void defaults(RecipeSpec spec) { - spec.recipe(new UseSdkManJavaVersion()); - } - - @DocumentExample - @Test - void createSdkmanrcFromMultipleJavaVersions() { - rewriteRun( - mavenProject("test", - srcMainJava( - version( - java( - """ - package com.example.core; - - class CoreService { - void processData() { - System.out.println("Processing data..."); - } - } - """ - ), 17) - ), - srcTestJava( - version( - java( - """ - package com.example.web; - - class WebService { - void serveRequests() { - System.out.println("Serving requests..."); - } - } - """ - ), 21) - ) - ), - // Expected .sdkmanrc file should be created with the highest Java version found - text( - doesNotExist(), - """ - java=21 - """, - sourceSpecs -> sourceSpecs.path(Path.of(".sdkmanrc")) - ) - ); - } - - @Test - void updateExistingSdkmanrc() { - rewriteRun( - mavenProject( - "test", - srcMainJava( - version( - java( - """ - package com.example; - - class Service { - void doWork() { - System.out.println("Working..."); - } - } - """ - ), 17) - ) - ), - // Existing .sdkmanrc with older Java version - text( - """ - java=11 - maven=3.8.6 - """, - """ - java=17 - maven=3.8.6 - """, - sourceSpecs -> sourceSpecs.path(Path.of(".sdkmanrc")) - ) - ); - } - - @Test - void noChangeWhenSdkmanrcAlreadyCorrect() { - rewriteRun( - mavenProject( - "test", - srcMainJava( - version( - java( - """ - package com.example; - - class Service { - void doWork() { - System.out.println("Working..."); - } - } - """ - ), 17) - ) - ), - // .sdkmanrc already has correct Java version - text( - """ - java=17 - maven=3.9.4 - """, - sourceSpecs -> sourceSpecs.path(Path.of(".sdkmanrc")) - ) - ); - } - - @Test - void noJavaVersionMarkerMeansNoFileCreated() { - rewriteRun( - mavenProject( - "test", - // Expected .sdkmanrc file should use Java 8 as fallback - text(""" - non java version text - """) - ) - ); - } - - @Test - void singleJavaVersionProject() { - rewriteRun( - mavenProject( - "test", - srcMainJava( - version( - java( - """ - package com.example; - - class ModernService { - void useNewFeatures() { - System.out.println("Using Java 19 features..."); - } - } - """ - ), 19) - ) - ), - text( - null, - """ - java=19 - """, - sourceSpecs -> sourceSpecs.path(Path.of(".sdkmanrc")) - ) - ); - } - - @Test - void multipleSourceSetsHighestVersion() { - rewriteRun( - mavenProject( - "test", - srcMainJava( - version( - java( - """ - package com.example.main; - - class MainService { - void mainWork() { - System.out.println("Main work..."); - } - } - """ - ), 17) - ), - srcTestJava( - version( - java( - """ - package com.example.test; - - class TestHelper { - void testWork() { - System.out.println("Test work..."); - } - } - """ - ), 21) - ) - ), - text( - null, - """ - java=21 - """, - sourceSpecs -> sourceSpecs.path(Path.of(".sdkmanrc")) - ) - ); - } - - @Test - void addJavaVersionToExistingFile() { - rewriteRun( - mavenProject( - "test", - srcMainJava( - version( - java( - """ - package com.example; - - class Service { - void doWork() { - System.out.println("Working..."); - } - } - """ - ), 17) - ) - ), - // Existing .sdkmanrc without Java version - text( - """ - maven=3.9.4 - gradle=7.6 - """, - """ - maven=3.9.4 - gradle=7.6 - java=17 - """, - sourceSpecs -> sourceSpecs.path(Path.of(".sdkmanrc")) - ) - ); - } -} From 9ecd7603a243140dd884820a90f012e7917cadfb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 20 Aug 2025 01:10:45 +0000 Subject: [PATCH 3/3] Fix test failures - improve version detection and property value updates Co-authored-by: TimeBather <25410512+TimeBather@users.noreply.github.com> --- .../yourorg/MinecraftVersionMigration.java | 69 +++++++++++++------ 1 file changed, 48 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/yourorg/MinecraftVersionMigration.java b/src/main/java/com/yourorg/MinecraftVersionMigration.java index a6aaa57..e923db4 100644 --- a/src/main/java/com/yourorg/MinecraftVersionMigration.java +++ b/src/main/java/com/yourorg/MinecraftVersionMigration.java @@ -20,7 +20,8 @@ import org.openrewrite.*; import org.openrewrite.groovy.GroovyIsoVisitor; import org.openrewrite.java.tree.J; -import org.openrewrite.properties.ChangePropertyValue; +import org.openrewrite.properties.PropertiesIsoVisitor; +import org.openrewrite.properties.tree.Properties; import java.util.regex.Pattern; @@ -63,7 +64,14 @@ public J.Literal visitLiteral(J.Literal literal, ExecutionContext ctx) { if (containsMinecraftVersion(value)) { String updatedValue = value.replace(currentVersion, targetVersion); if (!updatedValue.equals(value)) { - return l.withValue(updatedValue).withValueSource("\"" + updatedValue + "\""); + // Preserve the original quote style from the source + String valueSource = l.getValueSource(); + if (valueSource != null) { + char quoteChar = valueSource.charAt(0); + return l.withValue(updatedValue).withValueSource(quoteChar + updatedValue + quoteChar); + } else { + return l.withValue(updatedValue).withValueSource("\"" + updatedValue + "\""); + } } } } @@ -71,31 +79,50 @@ public J.Literal visitLiteral(J.Literal literal, ExecutionContext ctx) { } } ), - // Handle gradle.properties files - basic version properties + // Handle gradle.properties files Preconditions.check( new FindSourceFiles("**/gradle.properties"), - new ChangePropertyValue("minecraft_version", targetVersion, null, null, null).getVisitor() - ), - // Handle more gradle.properties patterns - Preconditions.check( - new FindSourceFiles("**/gradle.properties"), - new ChangePropertyValue("mc_version", targetVersion, null, null, null).getVisitor() + new PropertiesIsoVisitor() { + @Override + public Properties.Entry visitEntry(Properties.Entry entry, ExecutionContext ctx) { + Properties.Entry e = super.visitEntry(entry, ctx); + if (e.getValue() instanceof Properties.Value) { + Properties.Value value = (Properties.Value) e.getValue(); + if (value.getText() != null && value.getText().contains(currentVersion)) { + String updatedValue = value.getText().replace(currentVersion, targetVersion); + if (!updatedValue.equals(value.getText())) { + return e.withValue(value.withText(updatedValue)); + } + } + } + return e; + } + } ) ); } private boolean containsMinecraftVersion(String value) { - // Check if the string contains version patterns commonly used in Minecraft projects - return value.contains(currentVersion) && ( - value.toLowerCase().contains("minecraft") || - value.toLowerCase().contains("forge") || - value.toLowerCase().contains("neoforge") || - value.toLowerCase().contains("fabric") || - value.toLowerCase().contains("quilt") || - // Version patterns like "net.minecraft:server:1.20.1" - Pattern.compile("net\\.minecraft[\\w.]*:" + Pattern.quote(currentVersion)).matcher(value).find() || - // Version patterns in dependency strings - Pattern.compile(":['\"]?" + Pattern.quote(currentVersion) + "['\"]?").matcher(value).find() - ); + // Check if the string contains the current version + if (!value.contains(currentVersion)) { + return false; + } + + // For simple cases like "1.20.1" assignments, always allow + if (value.equals(currentVersion)) { + return true; + } + + // For strings that might contain the version, check common patterns + return value.toLowerCase().contains("minecraft") || + value.toLowerCase().contains("forge") || + value.toLowerCase().contains("neoforge") || + value.toLowerCase().contains("fabric") || + value.toLowerCase().contains("quilt") || + value.toLowerCase().contains("official") || // mappings + // Version patterns like "net.minecraft:server:1.20.1" + Pattern.compile("net\\.minecraft[\\w.]*:" + Pattern.quote(currentVersion)).matcher(value).find() || + // Version patterns in dependency strings + Pattern.compile(":['\"]?" + Pattern.quote(currentVersion) + "['\"]?").matcher(value).find(); } } \ No newline at end of file