Skip to content

Commit 1fadbe2

Browse files
committed
Merge remote-tracking branch 'origin/main'
2 parents 2b7ac8b + 602ec88 commit 1fadbe2

222 files changed

Lines changed: 12649 additions & 5172 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

build.gradle.kts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import java.util.Calendar
2+
13
plugins {
24
id("org.openrewrite.build.root") version "latest.release"
35
id("org.openrewrite.build.java-base") version "latest.release"
@@ -23,6 +25,29 @@ allprojects {
2325
description = "Eliminate tech-debt. Automatically."
2426
}
2527

28+
subprojects {
29+
tasks.withType<JavaExec>().configureEach {
30+
if (name == "generateAntlrSources") {
31+
doLast {
32+
val idx = args?.indexOf("-o") ?: return@doLast
33+
if (idx < 0 || idx + 1 >= args!!.size) return@doLast
34+
val rootPrefix = rootProject.projectDir.absolutePath + "/"
35+
val year = Calendar.getInstance().get(Calendar.YEAR)
36+
val licenseHeader = "/*\n" + rootProject.file("gradle/licenseHeader.txt")
37+
.readText().trim()
38+
.replace("\${year}", year.toString())
39+
.lines()
40+
.joinToString("\n") { " * $it".trimEnd() } + "\n */\n"
41+
project.file(args!![idx + 1]).walk().filter { it.extension == "java" }.forEach { file ->
42+
file.writeText(licenseHeader + file.readLines().joinToString("\n") { line ->
43+
line.trimEnd().replace("// Generated from $rootPrefix", "// Generated from ")
44+
} + "\n")
45+
}
46+
}
47+
}
48+
}
49+
}
50+
2651
// Use this task locally between different dependency check runs to have updated analysis:
2752
// OSSINDEX_PASSWORD=... OSSINDEX_USERNAME=... gradle cleanReports dCAg --no-parallel
2853
tasks.register<Delete>("cleanReports") {
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.0-bin.zip
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip
44
networkTimeout=10000
55
validateDistributionUrl=true
66
zipStoreBase=GRADLE_USER_HOME
77
zipStorePath=wrapper/dists
8-
distributionSha256Sum=0d585f69da091fc5b2beced877feab55a3064d43b8a1d46aeb07996b0915e0e0
8+
distributionSha256Sum=b266d5ff6b90eada6dc3b20cb090e3731302e553a27c5d3e4df1f0d76beaff06

rewrite-core/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,5 @@ tasks.withType<Javadoc> {
4646
// symbol: method onConstructor_()
4747
// location: @interface AllArgsConstructor
4848
// 1 error
49-
exclude("**/RpcObjectData.java")
49+
exclude("**/RpcObjectData.java", "**/RecipeDescriptor.java")
5050
}

rewrite-core/src/main/java/org/openrewrite/Checksum.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
import java.io.IOException;
2727
import java.io.InputStream;
28+
import java.io.UncheckedIOException;
2829
import java.net.URI;
2930
import java.nio.charset.StandardCharsets;
3031
import java.nio.file.Files;
@@ -83,6 +84,10 @@ public static Checksum fromUri(HttpSender httpSender, URI uri, String algorithm)
8384
.withMethod(HttpSender.Method.GET)
8485
.build();
8586
try (HttpSender.Response response = httpSender.send(request)) {
87+
if (!response.isSuccessful()) {
88+
throw new UncheckedIOException(new IOException(
89+
"Failed to download checksum from " + uri + ": HTTP " + response.getCode()));
90+
}
8691
String hexString = new String(response.getBodyAsBytes(), StandardCharsets.UTF_8);
8792
return Checksum.fromHex(algorithm, hexString);
8893
}

rewrite-core/src/main/java/org/openrewrite/LargeSourceSet.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,4 +99,17 @@ default void afterCycle(boolean lastCycle) {
9999
*/
100100
@Nullable
101101
SourceFile getBefore(Path sourcePath);
102+
103+
/**
104+
* Called when a recipe's {@code generate()} produces a file whose path collides with
105+
* an existing source file or with another generated file. The generated file is silently
106+
* dropped. Implementations may override this to detect the collision (e.g. the test
107+
* framework raises an assertion failure).
108+
*
109+
* @param sourcePath The colliding source path.
110+
* @param existingFile {@code true} if the collision is with an existing source file,
111+
* {@code false} if it's with another generated file.
112+
*/
113+
default void onGenerateCollision(Path sourcePath, boolean existingFile) {
114+
}
102115
}

rewrite-core/src/main/java/org/openrewrite/Recipe.java

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -203,8 +203,6 @@ public String getInstanceNameSuffix() {
203203
* A set of strings used for categorizing related recipes. For example
204204
* "testing", "junit", "spring". Any individual tag should consist of a
205205
* single word, all lowercase.
206-
*
207-
* @return The tags.
208206
*/
209207
@Getter
210208
final Set<String> tags = emptySet();
@@ -225,11 +223,21 @@ public final RecipeDescriptor getDescriptor() {
225223

226224
protected RecipeDescriptor createRecipeDescriptor() {
227225
List<OptionDescriptor> options = getOptionDescriptors();
228-
ArrayList<RecipeDescriptor> recipeList1 = new ArrayList<>();
229-
for (Recipe next : getRecipeList()) {
230-
recipeList1.add(next.getDescriptor());
226+
List<RecipeDescriptor> preconditionDescriptors = emptyList();
227+
if (this instanceof RecipePreconditions) {
228+
RecipePreconditions recipeWithPreconditions = (RecipePreconditions) this;
229+
List<Recipe> preconditions = recipeWithPreconditions.getPreconditions();
230+
preconditionDescriptors = new ArrayList<>(preconditions.size());
231+
for (Recipe precondition : preconditions) {
232+
preconditionDescriptors.add(precondition.getDescriptor());
233+
}
234+
}
235+
236+
List<Recipe> recipeList = getRecipeList();
237+
List<RecipeDescriptor> recipeDescriptors = new ArrayList<>(recipeList.size());
238+
for (Recipe next : recipeList) {
239+
recipeDescriptors.add(next.getDescriptor());
231240
}
232-
recipeList1.trimToSize();
233241

234242
URI recipeSource;
235243
try {
@@ -239,7 +247,7 @@ protected RecipeDescriptor createRecipeDescriptor() {
239247
}
240248

241249
return new RecipeDescriptor(getName(), getDisplayName(), getInstanceName(), getDescription(), getTags(),
242-
getEstimatedEffortPerOccurrence(), options, recipeList1, getDataTableDescriptors(),
250+
getEstimatedEffortPerOccurrence(), options, preconditionDescriptors, recipeDescriptors, getDataTableDescriptors(),
243251
getMaintainers(), getContributors(), getExamples(), recipeSource);
244252
}
245253

rewrite-core/src/main/java/org/openrewrite/Singleton.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,8 @@ public class Singleton extends Recipe {
8181
"The easiest way is to use Lombok's `@Value` annotation on your recipe class, which automatically " +
8282
"generates correct `equals()` and `hashCode()` implementations based on all fields.";
8383

84-
@Nullable Integer recipeIndex;
85-
8684
public boolean isSingleton(ExecutionContext ctx) {
87-
if (recipeIndex == null) {
88-
recipeIndex = ctx.getCycleDetails().getRecipePosition();
89-
}
85+
Integer recipeIndex = ctx.computeMessageIfAbsent(getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(this)), key -> ctx.getCycleDetails().getRecipePosition());
9086
return recipeIndex == ctx.getCycleDetails().getRecipePosition();
9187
}
9288

rewrite-core/src/main/java/org/openrewrite/config/DeclarativeRecipe.java

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
import static org.openrewrite.Validated.invalid;
3333

3434
@RequiredArgsConstructor
35-
public class DeclarativeRecipe extends ScanningRecipe<DeclarativeRecipe.Accumulator> {
35+
public class DeclarativeRecipe extends ScanningRecipe<DeclarativeRecipe.Accumulator> implements RecipePreconditions {
3636
@Getter
3737
private final String name;
3838

@@ -161,17 +161,16 @@ private void initializeDeclarativeRecipe(DeclarativeRecipe declarativeRecipe, St
161161
}
162162
}
163163

164-
@SuppressWarnings("NotNullFieldNotInitialized")
165164
@JsonIgnore
166-
private transient Accumulator accumulator;
165+
private transient ThreadLocal<Accumulator> accumulator = new ThreadLocal<>();
167166

168167
@Override
169168
public Accumulator getInitialValue(ExecutionContext ctx) {
170169
Accumulator acc = new Accumulator();
171170
for (Recipe precondition : preconditions) {
172171
registerNestedScanningRecipes(precondition, acc, ctx);
173172
}
174-
accumulator = acc;
173+
accumulator.set(acc);
175174
return acc;
176175
}
177176

@@ -187,7 +186,6 @@ private void registerNestedScanningRecipes(Recipe recipe, Accumulator acc, Execu
187186
@Override
188187
public TreeVisitor<?, ExecutionContext> getScanner(Accumulator acc) {
189188
return new TreeVisitor<Tree, ExecutionContext>() {
190-
@SuppressWarnings({"rawtypes", "unchecked"})
191189
@Override
192190
public @Nullable Tree visit(@Nullable Tree tree, ExecutionContext ctx) {
193191
for (Recipe precondition : preconditions) {
@@ -251,7 +249,7 @@ public boolean isAcceptable(SourceFile sourceFile, ExecutionContext ctx) {
251249

252250
@EqualsAndHashCode(callSuper = false)
253251
@Value
254-
static class BellwetherDecoratedRecipe extends Recipe implements DelegatingRecipe {
252+
static class BellwetherDecoratedRecipe extends Recipe implements DelegatingRecipe, RecipePreconditions {
255253

256254
DeclarativeRecipe.PreconditionBellwether bellwether;
257255
Recipe delegate;
@@ -350,11 +348,19 @@ public Validated<Object> validate(ExecutionContext ctx) {
350348
public Collection<Validated<Object>> validateAll(ExecutionContext ctx, Collection<Validated<Object>> acc) {
351349
return delegate.validateAll(ctx, acc);
352350
}
351+
352+
@Override
353+
public List<Recipe> getPreconditions() {
354+
if (delegate instanceof RecipePreconditions) {
355+
return ((RecipePreconditions) delegate).getPreconditions();
356+
}
357+
return emptyList();
358+
}
353359
}
354360

355361
@Value
356362
@EqualsAndHashCode(callSuper = false)
357-
static class BellwetherDecoratedScanningRecipe<T> extends ScanningRecipe<T> implements DelegatingRecipe {
363+
static class BellwetherDecoratedScanningRecipe<T> extends ScanningRecipe<T> implements DelegatingRecipe, RecipePreconditions {
358364

359365
DeclarativeRecipe.PreconditionBellwether bellwether;
360366
ScanningRecipe<T> delegate;
@@ -468,6 +474,14 @@ public Validated<Object> validate(ExecutionContext ctx) {
468474
public Collection<Validated<Object>> validateAll(ExecutionContext ctx, Collection<Validated<Object>> acc) {
469475
return delegate.validateAll(ctx, acc);
470476
}
477+
478+
@Override
479+
public List<Recipe> getPreconditions() {
480+
if (delegate instanceof RecipePreconditions) {
481+
return ((RecipePreconditions) delegate).getPreconditions();
482+
}
483+
return emptyList();
484+
}
471485
}
472486

473487
@Override
@@ -494,7 +508,8 @@ private TreeVisitor<?, ExecutionContext> orVisitors(Recipe recipe) {
494508
//noinspection rawtypes
495509
ScanningRecipe scanning = (ScanningRecipe) recipe;
496510
//noinspection unchecked
497-
conditions.add(scanning.getVisitor(accumulator.recipeToAccumulator.get(scanning)));
511+
Accumulator acc = accumulator.get();
512+
conditions.add(scanning.getVisitor(acc != null ? acc.recipeToAccumulator.get(scanning) : null));
498513
} else {
499514
conditions.add(recipe.getVisitor());
500515
}
@@ -587,15 +602,31 @@ private static class LazyLoadedRecipe extends Recipe {
587602
String description = "Recipe that is loaded lazily.";
588603
}
589604

605+
@Override
606+
public void onComplete(ExecutionContext ctx) {
607+
accumulator.remove();
608+
}
609+
610+
@Override
611+
public DeclarativeRecipe clone() {
612+
DeclarativeRecipe cloned = (DeclarativeRecipe) super.clone();
613+
cloned.accumulator = new ThreadLocal<>();
614+
return cloned;
615+
}
616+
590617
@Override
591618
protected RecipeDescriptor createRecipeDescriptor() {
592-
List<RecipeDescriptor> recipeList = new ArrayList<>();
593-
for (Recipe childRecipe : getRecipeList()) {
594-
recipeList.add(childRecipe.getDescriptor());
619+
List<RecipeDescriptor> preconditionDescriptors = new ArrayList<>();
620+
for (Recipe childRecipe : preconditions) {
621+
preconditionDescriptors.add(childRecipe.getDescriptor());
622+
}
623+
List<RecipeDescriptor> recipeDescriptors = new ArrayList<>();
624+
for (Recipe childRecipe : recipeList) {
625+
recipeDescriptors.add(childRecipe.getDescriptor());
595626
}
596627
return new RecipeDescriptor(getName(), getDisplayName(), getInstanceName(), getDescription() != null ? getDescription() : "",
597628
getTags(), getEstimatedEffortPerOccurrence(),
598-
emptyList(), recipeList, getDataTableDescriptors(), getMaintainers(), getContributors(),
629+
emptyList(), preconditionDescriptors, recipeDescriptors, getDataTableDescriptors(), getMaintainers(), getContributors(),
599630
getExamples(), source);
600631
}
601632

rewrite-core/src/main/java/org/openrewrite/config/RecipeDescriptor.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
*/
1616
package org.openrewrite.config;
1717

18+
import com.fasterxml.jackson.annotation.JsonCreator;
19+
import lombok.AllArgsConstructor;
1820
import lombok.EqualsAndHashCode;
1921
import lombok.Value;
2022
import lombok.With;
@@ -36,6 +38,7 @@
3638

3739
@Value
3840
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
41+
@AllArgsConstructor(onConstructor = @__(@JsonCreator))
3942
public class RecipeDescriptor {
4043
@EqualsAndHashCode.Include
4144
String name;
@@ -57,6 +60,9 @@ public class RecipeDescriptor {
5760
@EqualsAndHashCode.Include
5861
List<OptionDescriptor> options;
5962

63+
@With
64+
List<RecipeDescriptor> preconditions;
65+
6066
@With
6167
List<RecipeDescriptor> recipeList;
6268

@@ -73,6 +79,16 @@ public class RecipeDescriptor {
7379
@Deprecated
7480
URI source;
7581

82+
@Deprecated
83+
public RecipeDescriptor(String name, String displayName, String instanceName, String description,
84+
Set<String> tags, @Nullable Duration estimatedEffortPerOccurrence,
85+
List<OptionDescriptor> options, List<RecipeDescriptor> recipeList,
86+
List<DataTableDescriptor> dataTables, List<Maintainer> maintainers,
87+
List<Contributor> contributors, List<RecipeExample> examples, URI source) {
88+
this(name, displayName, instanceName, description, tags, estimatedEffortPerOccurrence,
89+
options, emptyList(), recipeList, dataTables, maintainers, contributors, examples, source);
90+
}
91+
7692
/**
7793
* @param env Provides a source of category descriptors to build category names from more
7894
* than just the name segments.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright 2026 the original author or authors.
3+
* <p>
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.openrewrite.config;
17+
18+
import org.openrewrite.Recipe;
19+
20+
import java.util.List;
21+
22+
/**
23+
* Indicates that a recipe exposes a list of recipes used as preconditions.
24+
* This is purely informational and not taken into consideration by recipe execution.
25+
* Imperative recipes interact with preconditions as implementation details of their visitor(s).
26+
* There is no reason for an imperative recipe to implement this, it will not affect the behavior of the recipe in any way.
27+
*/
28+
public interface RecipePreconditions {
29+
List<Recipe> getPreconditions();
30+
}

0 commit comments

Comments
 (0)