();
+ for (String line : allLines.split("\n")) {
+ String trimmed = line.trim();
+ if (!trimmed.isEmpty() && !trimmed.startsWith("#")) {
+ dependencies.add(trimmed);
+ }
+ }
+ if (dependencies.isEmpty()) {
+ throw new IllegalArgumentException("No dependencies defined in lockfile " + lockfileResourcePath);
+ }
+ return dependencies;
+ }
+
+ /**
+ * Returns the classpath resource path of an embedded lockfile for the given formatter version.
+ *
+ * Defaults to the same layout {@link EclipseBasedStepBuilder} uses -- {@code v.lockfile}
+ * inside a directory named after the formatter -- so a step picks up lockfile support simply by
+ * having its lockfiles checked in. Versions with no embedded lockfile fall back to P2
+ * provisioning, which is what keeps Eclipse releases newer than Spotless usable.
+ *
+ * Overriding implementations should return an absolute classpath resource path (starting with
+ * {@code /}) that is compatible with {@link Class#getResourceAsStream(String)}.
+ *
+ * @return absolute classpath resource path of the embedded lockfile, or {@code null} to always
+ * use P2 provisioning
+ */
+ protected @Nullable String lockfileResourcePath(String version) {
+ return "/" + EclipseBasedStepBuilder.ECLIPSE_FORMATTER_RESOURCES + "/" + formatterName.replace(' ', '_') + "/v" + version + ".lockfile";
+ }
+
private P2Model createModelWithMirrors() {
P2Model model = model(formatterVersion);
if (p2Mirrors.isEmpty()) {
diff --git a/lib-extra/src/main/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStep.java b/lib-extra/src/main/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStep.java
index d140d03042..83a3ea8d55 100644
--- a/lib-extra/src/main/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStep.java
+++ b/lib-extra/src/main/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStep.java
@@ -35,7 +35,7 @@ public final class EclipseJdtFormatterStep {
private EclipseJdtFormatterStep() {}
private static final String NAME = "eclipse jdt formatter";
- private static final Jvm.Support JVM_SUPPORT = Jvm. support(NAME).add(17, "4.39");
+ private static final Jvm.Support JVM_SUPPORT = Jvm. support(NAME).add(17, "4.40");
public static String defaultVersion() {
return JVM_SUPPORT.getRecommendedFormatterVersion();
diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.10.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.10.lockfile
new file mode 100644
index 0000000000..2cbfe35e0f
--- /dev/null
+++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.10.lockfile
@@ -0,0 +1,16 @@
+# Spotless formatter based on Eclipse-JDT 4.10
+org.eclipse.jdt:org.eclipse.jdt.core:3.16.0
+org.eclipse.platform:org.eclipse.core.commands:3.9.200
+org.eclipse.platform:org.eclipse.core.contenttype:3.7.200
+org.eclipse.platform:org.eclipse.core.expressions:3.6.200
+org.eclipse.platform:org.eclipse.core.filesystem:1.7.200
+org.eclipse.platform:org.eclipse.core.jobs:3.10.200
+org.eclipse.platform:org.eclipse.core.resources:3.13.200
+org.eclipse.platform:org.eclipse.core.runtime:3.15.100
+org.eclipse.platform:org.eclipse.equinox.app:1.4.0
+org.eclipse.platform:org.eclipse.equinox.common:3.10.200
+org.eclipse.platform:org.eclipse.equinox.preferences:3.7.200
+org.eclipse.platform:org.eclipse.equinox.registry:3.8.200
+org.eclipse.platform:org.eclipse.equinox.supplement:1.8.100
+org.eclipse.platform:org.eclipse.osgi:3.13.200
+org.eclipse.platform:org.eclipse.text:3.8.0
diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.11.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.11.lockfile
new file mode 100644
index 0000000000..47a5454c25
--- /dev/null
+++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.11.lockfile
@@ -0,0 +1,16 @@
+# Spotless formatter based on Eclipse-JDT 4.11
+org.eclipse.jdt:org.eclipse.jdt.core:3.17.0
+org.eclipse.platform:org.eclipse.core.commands:3.9.300
+org.eclipse.platform:org.eclipse.core.contenttype:3.7.300
+org.eclipse.platform:org.eclipse.core.expressions:3.6.300
+org.eclipse.platform:org.eclipse.core.filesystem:1.7.300
+org.eclipse.platform:org.eclipse.core.jobs:3.10.300
+org.eclipse.platform:org.eclipse.core.resources:3.13.300
+org.eclipse.platform:org.eclipse.core.runtime:3.15.200
+org.eclipse.platform:org.eclipse.equinox.app:1.4.100
+org.eclipse.platform:org.eclipse.equinox.common:3.10.300
+org.eclipse.platform:org.eclipse.equinox.preferences:3.7.300
+org.eclipse.platform:org.eclipse.equinox.registry:3.8.300
+org.eclipse.platform:org.eclipse.equinox.supplement:1.8.200
+org.eclipse.platform:org.eclipse.osgi:3.13.300
+org.eclipse.platform:org.eclipse.text:3.8.100
diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.12.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.12.lockfile
new file mode 100644
index 0000000000..fae8e59670
--- /dev/null
+++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.12.lockfile
@@ -0,0 +1,16 @@
+# Spotless formatter based on Eclipse-JDT 4.12
+org.eclipse.jdt:org.eclipse.jdt.core:3.18.0
+org.eclipse.platform:org.eclipse.core.commands:3.9.400
+org.eclipse.platform:org.eclipse.core.contenttype:3.7.300
+org.eclipse.platform:org.eclipse.core.expressions:3.6.400
+org.eclipse.platform:org.eclipse.core.filesystem:1.7.400
+org.eclipse.platform:org.eclipse.core.jobs:3.10.400
+org.eclipse.platform:org.eclipse.core.resources:3.13.400
+org.eclipse.platform:org.eclipse.core.runtime:3.15.300
+org.eclipse.platform:org.eclipse.equinox.app:1.4.200
+org.eclipse.platform:org.eclipse.equinox.common:3.10.400
+org.eclipse.platform:org.eclipse.equinox.preferences:3.7.400
+org.eclipse.platform:org.eclipse.equinox.registry:3.8.400
+org.eclipse.platform:org.eclipse.equinox.supplement:1.8.300
+org.eclipse.platform:org.eclipse.osgi:3.14.0
+org.eclipse.platform:org.eclipse.text:3.8.200
diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.13.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.13.lockfile
new file mode 100644
index 0000000000..1c6f9d9b62
--- /dev/null
+++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.13.lockfile
@@ -0,0 +1,16 @@
+# Spotless formatter based on Eclipse-JDT 4.13
+org.eclipse.jdt:org.eclipse.jdt.core:3.19.0
+org.eclipse.platform:org.eclipse.core.commands:3.9.500
+org.eclipse.platform:org.eclipse.core.contenttype:3.7.400
+org.eclipse.platform:org.eclipse.core.expressions:3.6.500
+org.eclipse.platform:org.eclipse.core.filesystem:1.7.500
+org.eclipse.platform:org.eclipse.core.jobs:3.10.500
+org.eclipse.platform:org.eclipse.core.resources:3.13.500
+org.eclipse.platform:org.eclipse.core.runtime:3.16.0
+org.eclipse.platform:org.eclipse.equinox.app:1.4.300
+org.eclipse.platform:org.eclipse.equinox.common:3.10.500
+org.eclipse.platform:org.eclipse.equinox.preferences:3.7.500
+org.eclipse.platform:org.eclipse.equinox.registry:3.8.500
+org.eclipse.platform:org.eclipse.equinox.supplement:1.9.0
+org.eclipse.platform:org.eclipse.osgi:3.15.0
+org.eclipse.platform:org.eclipse.text:3.9.0
diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.14.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.14.lockfile
new file mode 100644
index 0000000000..7c237413de
--- /dev/null
+++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.14.lockfile
@@ -0,0 +1,16 @@
+# Spotless formatter based on Eclipse-JDT 4.14
+org.eclipse.jdt:org.eclipse.jdt.core:3.20.0
+org.eclipse.platform:org.eclipse.core.commands:3.9.600
+org.eclipse.platform:org.eclipse.core.contenttype:3.7.500
+org.eclipse.platform:org.eclipse.core.expressions:3.6.600
+org.eclipse.platform:org.eclipse.core.filesystem:1.7.600
+org.eclipse.platform:org.eclipse.core.jobs:3.10.600
+org.eclipse.platform:org.eclipse.core.resources:3.13.600
+org.eclipse.platform:org.eclipse.core.runtime:3.17.0
+org.eclipse.platform:org.eclipse.equinox.app:1.4.300
+org.eclipse.platform:org.eclipse.equinox.common:3.10.600
+org.eclipse.platform:org.eclipse.equinox.preferences:3.7.600
+org.eclipse.platform:org.eclipse.equinox.registry:3.8.600
+org.eclipse.platform:org.eclipse.equinox.supplement:1.9.100
+org.eclipse.platform:org.eclipse.osgi:3.15.100
+org.eclipse.platform:org.eclipse.text:3.10.0
diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.15.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.15.lockfile
new file mode 100644
index 0000000000..4ce8634ae9
--- /dev/null
+++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.15.lockfile
@@ -0,0 +1,16 @@
+# Spotless formatter based on Eclipse-JDT 4.15
+org.eclipse.jdt:org.eclipse.jdt.core:3.21.0
+org.eclipse.platform:org.eclipse.core.commands:3.9.700
+org.eclipse.platform:org.eclipse.core.contenttype:3.7.600
+org.eclipse.platform:org.eclipse.core.expressions:3.6.700
+org.eclipse.platform:org.eclipse.core.filesystem:1.7.700
+org.eclipse.platform:org.eclipse.core.jobs:3.10.700
+org.eclipse.platform:org.eclipse.core.resources:3.13.700
+org.eclipse.platform:org.eclipse.core.runtime:3.17.100
+org.eclipse.platform:org.eclipse.equinox.app:1.4.400
+org.eclipse.platform:org.eclipse.equinox.common:3.11.0
+org.eclipse.platform:org.eclipse.equinox.preferences:3.7.700
+org.eclipse.platform:org.eclipse.equinox.registry:3.8.700
+org.eclipse.platform:org.eclipse.equinox.supplement:1.9.200
+org.eclipse.platform:org.eclipse.osgi:3.15.200
+org.eclipse.platform:org.eclipse.text:3.10.100
diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.16.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.16.lockfile
new file mode 100644
index 0000000000..c2673bda4a
--- /dev/null
+++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.16.lockfile
@@ -0,0 +1,16 @@
+# Spotless formatter based on Eclipse-JDT 4.16
+org.eclipse.jdt:org.eclipse.jdt.core:3.22.0
+org.eclipse.platform:org.eclipse.core.commands:3.9.700
+org.eclipse.platform:org.eclipse.core.contenttype:3.7.700
+org.eclipse.platform:org.eclipse.core.expressions:3.6.800
+org.eclipse.platform:org.eclipse.core.filesystem:1.7.700
+org.eclipse.platform:org.eclipse.core.jobs:3.10.800
+org.eclipse.platform:org.eclipse.core.resources:3.13.700
+org.eclipse.platform:org.eclipse.core.runtime:3.18.0
+org.eclipse.platform:org.eclipse.equinox.app:1.4.500
+org.eclipse.platform:org.eclipse.equinox.common:3.12.0
+org.eclipse.platform:org.eclipse.equinox.preferences:3.8.0
+org.eclipse.platform:org.eclipse.equinox.registry:3.8.800
+org.eclipse.platform:org.eclipse.equinox.supplement:1.9.300
+org.eclipse.platform:org.eclipse.osgi:3.15.300
+org.eclipse.platform:org.eclipse.text:3.10.200
diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.17.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.17.lockfile
new file mode 100644
index 0000000000..5de970283c
--- /dev/null
+++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.17.lockfile
@@ -0,0 +1,16 @@
+# Spotless formatter based on Eclipse-JDT 4.17
+org.eclipse.jdt:org.eclipse.jdt.core:3.23.0
+org.eclipse.platform:org.eclipse.core.commands:3.9.700
+org.eclipse.platform:org.eclipse.core.contenttype:3.7.800
+org.eclipse.platform:org.eclipse.core.expressions:3.7.0
+org.eclipse.platform:org.eclipse.core.filesystem:1.7.700
+org.eclipse.platform:org.eclipse.core.jobs:3.10.800
+org.eclipse.platform:org.eclipse.core.resources:3.13.800
+org.eclipse.platform:org.eclipse.core.runtime:3.19.0
+org.eclipse.platform:org.eclipse.equinox.app:1.5.0
+org.eclipse.platform:org.eclipse.equinox.common:3.13.0
+org.eclipse.platform:org.eclipse.equinox.preferences:3.8.0
+org.eclipse.platform:org.eclipse.equinox.registry:3.9.0
+org.eclipse.platform:org.eclipse.equinox.supplement:1.10.0
+org.eclipse.platform:org.eclipse.osgi:3.16.0
+org.eclipse.platform:org.eclipse.text:3.10.300
diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.18.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.18.lockfile
new file mode 100644
index 0000000000..af354c5d5e
--- /dev/null
+++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.18.lockfile
@@ -0,0 +1,16 @@
+# Spotless formatter based on Eclipse-JDT 4.18
+org.eclipse.jdt:org.eclipse.jdt.core:3.24.0
+org.eclipse.platform:org.eclipse.core.commands:3.9.800
+org.eclipse.platform:org.eclipse.core.contenttype:3.7.800
+org.eclipse.platform:org.eclipse.core.expressions:3.7.0
+org.eclipse.platform:org.eclipse.core.filesystem:1.7.700
+org.eclipse.platform:org.eclipse.core.jobs:3.10.1000
+org.eclipse.platform:org.eclipse.core.resources:3.13.900
+org.eclipse.platform:org.eclipse.core.runtime:3.20.0
+org.eclipse.platform:org.eclipse.equinox.app:1.5.0
+org.eclipse.platform:org.eclipse.equinox.common:3.14.0
+org.eclipse.platform:org.eclipse.equinox.preferences:3.8.100
+org.eclipse.platform:org.eclipse.equinox.registry:3.10.0
+org.eclipse.platform:org.eclipse.equinox.supplement:1.10.0
+org.eclipse.platform:org.eclipse.osgi:3.16.100
+org.eclipse.platform:org.eclipse.text:3.10.400
diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.19.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.19.lockfile
new file mode 100644
index 0000000000..d5bbcfa27f
--- /dev/null
+++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.19.lockfile
@@ -0,0 +1,16 @@
+# Spotless formatter based on Eclipse-JDT 4.19
+org.eclipse.jdt:org.eclipse.jdt.core:3.25.0
+org.eclipse.platform:org.eclipse.core.commands:3.9.800
+org.eclipse.platform:org.eclipse.core.contenttype:3.7.900
+org.eclipse.platform:org.eclipse.core.expressions:3.7.100
+org.eclipse.platform:org.eclipse.core.filesystem:1.7.700
+org.eclipse.platform:org.eclipse.core.jobs:3.10.1100
+org.eclipse.platform:org.eclipse.core.resources:3.14.0
+org.eclipse.platform:org.eclipse.core.runtime:3.20.100
+org.eclipse.platform:org.eclipse.equinox.app:1.5.100
+org.eclipse.platform:org.eclipse.equinox.common:3.14.100
+org.eclipse.platform:org.eclipse.equinox.preferences:3.8.200
+org.eclipse.platform:org.eclipse.equinox.registry:3.10.100
+org.eclipse.platform:org.eclipse.equinox.supplement:1.10.100
+org.eclipse.platform:org.eclipse.osgi:3.16.200
+org.eclipse.platform:org.eclipse.text:3.11.0
diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.20.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.20.lockfile
new file mode 100644
index 0000000000..445c9f007f
--- /dev/null
+++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.20.lockfile
@@ -0,0 +1,16 @@
+# Spotless formatter based on Eclipse-JDT 4.20
+org.eclipse.jdt:org.eclipse.jdt.core:3.26.0
+org.eclipse.platform:org.eclipse.core.commands:3.10.0
+org.eclipse.platform:org.eclipse.core.contenttype:3.7.1000
+org.eclipse.platform:org.eclipse.core.expressions:3.7.100
+org.eclipse.platform:org.eclipse.core.filesystem:1.9.0
+org.eclipse.platform:org.eclipse.core.jobs:3.11.0
+org.eclipse.platform:org.eclipse.core.resources:3.15.0
+org.eclipse.platform:org.eclipse.core.runtime:3.22.0
+org.eclipse.platform:org.eclipse.equinox.app:1.5.100
+org.eclipse.platform:org.eclipse.equinox.common:3.15.0
+org.eclipse.platform:org.eclipse.equinox.preferences:3.8.200
+org.eclipse.platform:org.eclipse.equinox.registry:3.10.200
+org.eclipse.platform:org.eclipse.equinox.supplement:1.10.100
+org.eclipse.platform:org.eclipse.osgi:3.16.300
+org.eclipse.platform:org.eclipse.text:3.12.0
diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.21.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.21.lockfile
new file mode 100644
index 0000000000..2a889feb18
--- /dev/null
+++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.21.lockfile
@@ -0,0 +1,16 @@
+# Spotless formatter based on Eclipse-JDT 4.21
+org.eclipse.jdt:org.eclipse.jdt.core:3.27.0
+org.eclipse.platform:org.eclipse.core.commands:3.10.100
+org.eclipse.platform:org.eclipse.core.contenttype:3.8.0
+org.eclipse.platform:org.eclipse.core.expressions:3.8.0
+org.eclipse.platform:org.eclipse.core.filesystem:1.9.100
+org.eclipse.platform:org.eclipse.core.jobs:3.12.0
+org.eclipse.platform:org.eclipse.core.resources:3.15.100
+org.eclipse.platform:org.eclipse.core.runtime:3.23.0
+org.eclipse.platform:org.eclipse.equinox.app:1.6.0
+org.eclipse.platform:org.eclipse.equinox.common:3.15.0
+org.eclipse.platform:org.eclipse.equinox.preferences:3.9.0
+org.eclipse.platform:org.eclipse.equinox.registry:3.11.0
+org.eclipse.platform:org.eclipse.equinox.supplement:1.10.200
+org.eclipse.platform:org.eclipse.osgi:3.17.0
+org.eclipse.platform:org.eclipse.text:3.12.0
diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.22.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.22.lockfile
new file mode 100644
index 0000000000..70fe49568f
--- /dev/null
+++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.22.lockfile
@@ -0,0 +1,16 @@
+# Spotless formatter based on Eclipse-JDT 4.22
+org.eclipse.jdt:org.eclipse.jdt.core:3.28.0
+org.eclipse.platform:org.eclipse.core.commands:3.10.100
+org.eclipse.platform:org.eclipse.core.contenttype:3.8.100
+org.eclipse.platform:org.eclipse.core.expressions:3.8.100
+org.eclipse.platform:org.eclipse.core.filesystem:1.9.200
+org.eclipse.platform:org.eclipse.core.jobs:3.12.0
+org.eclipse.platform:org.eclipse.core.resources:3.16.0
+org.eclipse.platform:org.eclipse.core.runtime:3.24.0
+org.eclipse.platform:org.eclipse.equinox.app:1.6.100
+org.eclipse.platform:org.eclipse.equinox.common:3.15.100
+org.eclipse.platform:org.eclipse.equinox.preferences:3.9.100
+org.eclipse.platform:org.eclipse.equinox.registry:3.11.100
+org.eclipse.platform:org.eclipse.equinox.supplement:1.10.300
+org.eclipse.platform:org.eclipse.osgi:3.17.100
+org.eclipse.platform:org.eclipse.text:3.12.0
diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.23.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.23.lockfile
new file mode 100644
index 0000000000..ea17095f44
--- /dev/null
+++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.23.lockfile
@@ -0,0 +1,16 @@
+# Spotless formatter based on Eclipse-JDT 4.23
+org.eclipse.jdt:org.eclipse.jdt.core:3.29.0
+org.eclipse.platform:org.eclipse.core.commands:3.10.100
+org.eclipse.platform:org.eclipse.core.contenttype:3.8.100
+org.eclipse.platform:org.eclipse.core.expressions:3.8.100
+org.eclipse.platform:org.eclipse.core.filesystem:1.9.300
+org.eclipse.platform:org.eclipse.core.jobs:3.12.100
+org.eclipse.platform:org.eclipse.core.resources:3.16.100
+org.eclipse.platform:org.eclipse.core.runtime:3.24.100
+org.eclipse.platform:org.eclipse.equinox.app:1.6.100
+org.eclipse.platform:org.eclipse.equinox.common:3.16.0
+org.eclipse.platform:org.eclipse.equinox.preferences:3.9.100
+org.eclipse.platform:org.eclipse.equinox.registry:3.11.100
+org.eclipse.platform:org.eclipse.equinox.supplement:1.10.400
+org.eclipse.platform:org.eclipse.osgi:3.17.200
+org.eclipse.platform:org.eclipse.text:3.12.0
diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.24.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.24.lockfile
new file mode 100644
index 0000000000..c8f527718d
--- /dev/null
+++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.24.lockfile
@@ -0,0 +1,17 @@
+# Spotless formatter based on Eclipse-JDT 4.24
+org.eclipse.jdt:org.eclipse.jdt.core:3.30.0
+org.eclipse.platform:org.eclipse.core.commands:3.10.200
+org.eclipse.platform:org.eclipse.core.contenttype:3.8.100
+org.eclipse.platform:org.eclipse.core.expressions:3.8.100
+org.eclipse.platform:org.eclipse.core.filesystem:1.9.400
+org.eclipse.platform:org.eclipse.core.jobs:3.13.0
+org.eclipse.platform:org.eclipse.core.resources:3.17.0
+org.eclipse.platform:org.eclipse.core.runtime:3.25.0
+org.eclipse.platform:org.eclipse.equinox.app:1.6.100
+org.eclipse.platform:org.eclipse.equinox.common:3.16.100
+org.eclipse.platform:org.eclipse.equinox.preferences:3.10.1
+org.eclipse.platform:org.eclipse.equinox.registry:3.11.100
+org.eclipse.platform:org.eclipse.equinox.supplement:1.10.500
+org.eclipse.platform:org.eclipse.osgi:3.18.0
+org.eclipse.platform:org.eclipse.text:3.12.100
+org.osgi:org.osgi.service.prefs:1.1.2
diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.25.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.25.lockfile
new file mode 100644
index 0000000000..c51599e651
--- /dev/null
+++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.25.lockfile
@@ -0,0 +1,17 @@
+# Spotless formatter based on Eclipse-JDT 4.25
+org.eclipse.jdt:org.eclipse.jdt.core:3.31.0
+org.eclipse.platform:org.eclipse.core.commands:3.10.200
+org.eclipse.platform:org.eclipse.core.contenttype:3.8.200
+org.eclipse.platform:org.eclipse.core.expressions:3.8.200
+org.eclipse.platform:org.eclipse.core.filesystem:1.9.500
+org.eclipse.platform:org.eclipse.core.jobs:3.13.100
+org.eclipse.platform:org.eclipse.core.resources:3.18.0
+org.eclipse.platform:org.eclipse.core.runtime:3.26.0
+org.eclipse.platform:org.eclipse.equinox.app:1.6.200
+org.eclipse.platform:org.eclipse.equinox.common:3.16.200
+org.eclipse.platform:org.eclipse.equinox.preferences:3.10.100
+org.eclipse.platform:org.eclipse.equinox.registry:3.11.200
+org.eclipse.platform:org.eclipse.equinox.supplement:1.10.600
+org.eclipse.platform:org.eclipse.osgi:3.18.100
+org.eclipse.platform:org.eclipse.text:3.12.200
+org.osgi:org.osgi.service.prefs:1.1.2
diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.26.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.26.lockfile
new file mode 100644
index 0000000000..b4c053796e
--- /dev/null
+++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.26.lockfile
@@ -0,0 +1,17 @@
+# Spotless formatter based on Eclipse-JDT 4.26
+org.eclipse.jdt:org.eclipse.jdt.core:3.32.0
+org.eclipse.platform:org.eclipse.core.commands:3.10.300
+org.eclipse.platform:org.eclipse.core.contenttype:3.8.200
+org.eclipse.platform:org.eclipse.core.expressions:3.8.200
+org.eclipse.platform:org.eclipse.core.filesystem:1.9.500
+org.eclipse.platform:org.eclipse.core.jobs:3.13.200
+org.eclipse.platform:org.eclipse.core.resources:3.18.100
+org.eclipse.platform:org.eclipse.core.runtime:3.26.100
+org.eclipse.platform:org.eclipse.equinox.app:1.6.200
+org.eclipse.platform:org.eclipse.equinox.common:3.17.0
+org.eclipse.platform:org.eclipse.equinox.preferences:3.10.100
+org.eclipse.platform:org.eclipse.equinox.registry:3.11.200
+org.eclipse.platform:org.eclipse.equinox.supplement:1.10.600
+org.eclipse.platform:org.eclipse.osgi:3.18.200
+org.eclipse.platform:org.eclipse.text:3.12.300
+org.osgi:org.osgi.service.prefs:1.1.2
diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.27.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.27.lockfile
new file mode 100644
index 0000000000..a50ad530ef
--- /dev/null
+++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.27.lockfile
@@ -0,0 +1,18 @@
+# Spotless formatter based on Eclipse-JDT 4.27
+org.eclipse.jdt:org.eclipse.jdt.core:3.33.0
+org.eclipse.jdt:ecj:3.33.0
+org.eclipse.platform:org.eclipse.core.commands:3.10.400
+org.eclipse.platform:org.eclipse.core.contenttype:3.8.200
+org.eclipse.platform:org.eclipse.core.expressions:3.8.200
+org.eclipse.platform:org.eclipse.core.filesystem:1.9.500
+org.eclipse.platform:org.eclipse.core.jobs:3.13.300
+org.eclipse.platform:org.eclipse.core.resources:3.18.200
+org.eclipse.platform:org.eclipse.core.runtime:3.26.100
+org.eclipse.platform:org.eclipse.equinox.app:1.6.200
+org.eclipse.platform:org.eclipse.equinox.common:3.17.100
+org.eclipse.platform:org.eclipse.equinox.preferences:3.10.200
+org.eclipse.platform:org.eclipse.equinox.registry:3.11.200
+org.eclipse.platform:org.eclipse.equinox.supplement:1.10.600
+org.eclipse.platform:org.eclipse.osgi:3.18.300
+org.eclipse.platform:org.eclipse.text:3.12.300
+org.osgi:org.osgi.service.prefs:1.1.2
diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.28.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.28.lockfile
new file mode 100644
index 0000000000..19746c78b2
--- /dev/null
+++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.28.lockfile
@@ -0,0 +1,18 @@
+# Spotless formatter based on Eclipse-JDT 4.28
+org.eclipse.jdt:org.eclipse.jdt.core:3.34.0
+org.eclipse.jdt:ecj:3.34.0
+org.eclipse.platform:org.eclipse.core.commands:3.11.0
+org.eclipse.platform:org.eclipse.core.contenttype:3.9.0
+org.eclipse.platform:org.eclipse.core.expressions:3.9.0
+org.eclipse.platform:org.eclipse.core.filesystem:1.10.0
+org.eclipse.platform:org.eclipse.core.jobs:3.14.0
+org.eclipse.platform:org.eclipse.core.resources:3.19.0
+org.eclipse.platform:org.eclipse.core.runtime:3.27.0
+org.eclipse.platform:org.eclipse.equinox.app:1.6.200
+org.eclipse.platform:org.eclipse.equinox.common:3.18.0
+org.eclipse.platform:org.eclipse.equinox.preferences:3.10.200
+org.eclipse.platform:org.eclipse.equinox.registry:3.11.200
+org.eclipse.platform:org.eclipse.equinox.supplement:1.10.700
+org.eclipse.platform:org.eclipse.osgi:3.18.400
+org.eclipse.platform:org.eclipse.text:3.13.0
+org.osgi:org.osgi.service.prefs:1.1.2
diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.29.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.29.lockfile
new file mode 100644
index 0000000000..66b277c6c9
--- /dev/null
+++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.29.lockfile
@@ -0,0 +1,18 @@
+# Spotless formatter based on Eclipse-JDT 4.29
+org.eclipse.jdt:org.eclipse.jdt.core:3.35.0
+org.eclipse.jdt:ecj:3.35.0
+org.eclipse.platform:org.eclipse.core.commands:3.11.100
+org.eclipse.platform:org.eclipse.core.contenttype:3.9.100
+org.eclipse.platform:org.eclipse.core.expressions:3.9.100
+org.eclipse.platform:org.eclipse.core.filesystem:1.10.100
+org.eclipse.platform:org.eclipse.core.jobs:3.15.0
+org.eclipse.platform:org.eclipse.core.resources:3.19.100
+org.eclipse.platform:org.eclipse.core.runtime:3.29.0
+org.eclipse.platform:org.eclipse.equinox.app:1.6.300
+org.eclipse.platform:org.eclipse.equinox.common:3.18.100
+org.eclipse.platform:org.eclipse.equinox.preferences:3.10.300
+org.eclipse.platform:org.eclipse.equinox.registry:3.11.300
+org.eclipse.platform:org.eclipse.equinox.supplement:1.10.700
+org.eclipse.platform:org.eclipse.osgi:3.18.500
+org.eclipse.platform:org.eclipse.text:3.13.100
+org.osgi:org.osgi.service.prefs:1.1.2
diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.30.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.30.lockfile
new file mode 100644
index 0000000000..21a07f7fb9
--- /dev/null
+++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.30.lockfile
@@ -0,0 +1,18 @@
+# Spotless formatter based on Eclipse-JDT 4.30
+org.eclipse.jdt:org.eclipse.jdt.core:3.36.0
+org.eclipse.jdt:ecj:3.36.0
+org.eclipse.platform:org.eclipse.core.commands:3.11.200
+org.eclipse.platform:org.eclipse.core.contenttype:3.9.200
+org.eclipse.platform:org.eclipse.core.expressions:3.9.200
+org.eclipse.platform:org.eclipse.core.filesystem:1.10.200
+org.eclipse.platform:org.eclipse.core.jobs:3.15.100
+org.eclipse.platform:org.eclipse.core.resources:3.20.0
+org.eclipse.platform:org.eclipse.core.runtime:3.30.0
+org.eclipse.platform:org.eclipse.equinox.app:1.6.400
+org.eclipse.platform:org.eclipse.equinox.common:3.18.200
+org.eclipse.platform:org.eclipse.equinox.preferences:3.10.400
+org.eclipse.platform:org.eclipse.equinox.registry:3.11.400
+org.eclipse.platform:org.eclipse.equinox.supplement:1.10.700
+org.eclipse.platform:org.eclipse.osgi:3.18.600
+org.eclipse.platform:org.eclipse.text:3.13.100
+org.osgi:org.osgi.service.prefs:1.1.2
diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.31.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.31.lockfile
new file mode 100644
index 0000000000..df8e7feb5f
--- /dev/null
+++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.31.lockfile
@@ -0,0 +1,18 @@
+# Spotless formatter based on Eclipse-JDT 4.31
+org.eclipse.jdt:org.eclipse.jdt.core:3.37.0
+org.eclipse.jdt:ecj:3.37.0
+org.eclipse.platform:org.eclipse.core.commands:3.12.0
+org.eclipse.platform:org.eclipse.core.contenttype:3.9.300
+org.eclipse.platform:org.eclipse.core.expressions:3.9.300
+org.eclipse.platform:org.eclipse.core.filesystem:1.10.300
+org.eclipse.platform:org.eclipse.core.jobs:3.15.200
+org.eclipse.platform:org.eclipse.core.resources:3.20.100
+org.eclipse.platform:org.eclipse.core.runtime:3.31.0
+org.eclipse.platform:org.eclipse.equinox.app:1.7.0
+org.eclipse.platform:org.eclipse.equinox.common:3.19.0
+org.eclipse.platform:org.eclipse.equinox.preferences:3.11.0
+org.eclipse.platform:org.eclipse.equinox.registry:3.12.0
+org.eclipse.platform:org.eclipse.equinox.supplement:1.10.800
+org.eclipse.platform:org.eclipse.osgi:3.19.0
+org.eclipse.platform:org.eclipse.text:3.14.0
+org.osgi:org.osgi.service.prefs:1.1.2
diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.32.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.32.lockfile
new file mode 100644
index 0000000000..155347ba8e
--- /dev/null
+++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.32.lockfile
@@ -0,0 +1,18 @@
+# Spotless formatter based on Eclipse-JDT 4.32
+org.eclipse.jdt:org.eclipse.jdt.core:3.38.0
+org.eclipse.jdt:ecj:3.38.0
+org.eclipse.platform:org.eclipse.core.commands:3.12.100
+org.eclipse.platform:org.eclipse.core.contenttype:3.9.400
+org.eclipse.platform:org.eclipse.core.expressions:3.9.400
+org.eclipse.platform:org.eclipse.core.filesystem:1.10.400
+org.eclipse.platform:org.eclipse.core.jobs:3.15.300
+org.eclipse.platform:org.eclipse.core.resources:3.20.200
+org.eclipse.platform:org.eclipse.core.runtime:3.31.100
+org.eclipse.platform:org.eclipse.equinox.app:1.7.100
+org.eclipse.platform:org.eclipse.equinox.common:3.19.100
+org.eclipse.platform:org.eclipse.equinox.preferences:3.11.100
+org.eclipse.platform:org.eclipse.equinox.registry:3.12.100
+org.eclipse.platform:org.eclipse.equinox.supplement:1.10.900
+org.eclipse.platform:org.eclipse.osgi:3.20.0
+org.eclipse.platform:org.eclipse.text:3.14.100
+org.osgi:org.osgi.service.prefs:1.1.2
diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.33.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.33.lockfile
new file mode 100644
index 0000000000..b62e8b4478
--- /dev/null
+++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.33.lockfile
@@ -0,0 +1,19 @@
+# Spotless formatter based on Eclipse-JDT 4.33
+org.eclipse.jdt:org.eclipse.jdt.core:3.39.0
+net.java.dev.jna:jna-platform:5.14.0
+org.eclipse.jdt:ecj:3.39.0
+org.eclipse.platform:org.eclipse.core.commands:3.12.200
+org.eclipse.platform:org.eclipse.core.contenttype:3.9.500
+org.eclipse.platform:org.eclipse.core.expressions:3.9.400
+org.eclipse.platform:org.eclipse.core.filesystem:1.11.0
+org.eclipse.platform:org.eclipse.core.jobs:3.15.400
+org.eclipse.platform:org.eclipse.core.resources:3.21.0
+org.eclipse.platform:org.eclipse.core.runtime:3.31.100
+org.eclipse.platform:org.eclipse.equinox.app:1.7.200
+org.eclipse.platform:org.eclipse.equinox.common:3.19.100
+org.eclipse.platform:org.eclipse.equinox.preferences:3.11.100
+org.eclipse.platform:org.eclipse.equinox.registry:3.12.100
+org.eclipse.platform:org.eclipse.equinox.supplement:1.11.0
+org.eclipse.platform:org.eclipse.osgi:3.21.0
+org.eclipse.platform:org.eclipse.text:3.14.100
+org.osgi:org.osgi.service.prefs:1.1.2
diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.34.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.34.lockfile
new file mode 100644
index 0000000000..9e7d990a4a
--- /dev/null
+++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.34.lockfile
@@ -0,0 +1,19 @@
+# Spotless formatter based on Eclipse-JDT 4.34
+org.eclipse.jdt:org.eclipse.jdt.core:3.40.0
+net.java.dev.jna:jna-platform:5.15.0
+org.eclipse.jdt:ecj:3.40.0
+org.eclipse.platform:org.eclipse.core.commands:3.12.200
+org.eclipse.platform:org.eclipse.core.contenttype:3.9.600
+org.eclipse.platform:org.eclipse.core.expressions:3.9.400
+org.eclipse.platform:org.eclipse.core.filesystem:1.11.100
+org.eclipse.platform:org.eclipse.core.jobs:3.15.400
+org.eclipse.platform:org.eclipse.core.resources:3.22.0
+org.eclipse.platform:org.eclipse.core.runtime:3.32.0
+org.eclipse.platform:org.eclipse.equinox.app:1.7.200
+org.eclipse.platform:org.eclipse.equinox.common:3.19.200
+org.eclipse.platform:org.eclipse.equinox.preferences:3.11.200
+org.eclipse.platform:org.eclipse.equinox.registry:3.12.200
+org.eclipse.platform:org.eclipse.equinox.supplement:1.11.100
+org.eclipse.platform:org.eclipse.osgi:3.22.0
+org.eclipse.platform:org.eclipse.text:3.14.200
+org.osgi:org.osgi.service.prefs:1.1.2
diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.35.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.35.lockfile
new file mode 100644
index 0000000000..16690074b4
--- /dev/null
+++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.35.lockfile
@@ -0,0 +1,19 @@
+# Spotless formatter based on Eclipse-JDT 4.35
+org.eclipse.jdt:org.eclipse.jdt.core:3.41.0
+net.java.dev.jna:jna-platform:5.16.0
+org.eclipse.jdt:ecj:3.41.0
+org.eclipse.platform:org.eclipse.core.commands:3.12.300
+org.eclipse.platform:org.eclipse.core.contenttype:3.9.600
+org.eclipse.platform:org.eclipse.core.expressions:3.9.400
+org.eclipse.platform:org.eclipse.core.filesystem:1.11.100
+org.eclipse.platform:org.eclipse.core.jobs:3.15.500
+org.eclipse.platform:org.eclipse.core.resources:3.22.100
+org.eclipse.platform:org.eclipse.core.runtime:3.33.0
+org.eclipse.platform:org.eclipse.equinox.app:1.7.300
+org.eclipse.platform:org.eclipse.equinox.common:3.20.0
+org.eclipse.platform:org.eclipse.equinox.preferences:3.11.300
+org.eclipse.platform:org.eclipse.equinox.registry:3.12.300
+org.eclipse.platform:org.eclipse.equinox.supplement:1.12.0
+org.eclipse.platform:org.eclipse.osgi:3.23.0
+org.eclipse.platform:org.eclipse.text:3.14.300
+org.osgi:org.osgi.service.prefs:1.1.2
diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.36.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.36.lockfile
new file mode 100644
index 0000000000..3b55b19835
--- /dev/null
+++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.36.lockfile
@@ -0,0 +1,19 @@
+# Spotless formatter based on Eclipse-JDT 4.36
+org.eclipse.jdt:org.eclipse.jdt.core:3.42.0
+net.java.dev.jna:jna-platform:5.17.0
+org.eclipse.jdt:ecj:3.42.0
+org.eclipse.platform:org.eclipse.core.commands:3.12.400
+org.eclipse.platform:org.eclipse.core.contenttype:3.9.600
+org.eclipse.platform:org.eclipse.core.expressions:3.9.400
+org.eclipse.platform:org.eclipse.core.filesystem:1.11.200
+org.eclipse.platform:org.eclipse.core.jobs:3.15.600
+org.eclipse.platform:org.eclipse.core.resources:3.22.200
+org.eclipse.platform:org.eclipse.core.runtime:3.33.100
+org.eclipse.platform:org.eclipse.equinox.app:1.7.400
+org.eclipse.platform:org.eclipse.equinox.common:3.20.100
+org.eclipse.platform:org.eclipse.equinox.preferences:3.11.400
+org.eclipse.platform:org.eclipse.equinox.registry:3.12.400
+org.eclipse.platform:org.eclipse.equinox.supplement:1.12.100
+org.eclipse.platform:org.eclipse.osgi:3.23.100
+org.eclipse.platform:org.eclipse.text:3.14.300
+org.osgi:org.osgi.service.prefs:1.1.2
diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.37.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.37.lockfile
new file mode 100644
index 0000000000..b91e896130
--- /dev/null
+++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.37.lockfile
@@ -0,0 +1,19 @@
+# Spotless formatter based on Eclipse-JDT 4.37
+org.eclipse.jdt:org.eclipse.jdt.core:3.43.0
+net.java.dev.jna:jna-platform:5.17.0
+org.eclipse.jdt:ecj:3.43.0
+org.eclipse.platform:org.eclipse.core.commands:3.12.400
+org.eclipse.platform:org.eclipse.core.contenttype:3.9.700
+org.eclipse.platform:org.eclipse.core.expressions:3.9.500
+org.eclipse.platform:org.eclipse.core.filesystem:1.11.300
+org.eclipse.platform:org.eclipse.core.jobs:3.15.700
+org.eclipse.platform:org.eclipse.core.resources:3.23.0
+org.eclipse.platform:org.eclipse.core.runtime:3.34.0
+org.eclipse.platform:org.eclipse.equinox.app:1.7.500
+org.eclipse.platform:org.eclipse.equinox.common:3.20.200
+org.eclipse.platform:org.eclipse.equinox.preferences:3.12.0
+org.eclipse.platform:org.eclipse.equinox.registry:3.12.500
+org.eclipse.platform:org.eclipse.equinox.supplement:1.12.100
+org.eclipse.platform:org.eclipse.osgi:3.23.200
+org.eclipse.platform:org.eclipse.text:3.14.400
+org.osgi:org.osgi.service.prefs:1.1.2
diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.38.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.38.lockfile
new file mode 100644
index 0000000000..f02f3494bf
--- /dev/null
+++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.38.lockfile
@@ -0,0 +1,19 @@
+# Spotless formatter based on Eclipse-JDT 4.38
+org.eclipse.jdt:org.eclipse.jdt.core:3.44.0
+net.java.dev.jna:jna-platform:5.18.1
+org.eclipse.jdt:ecj:3.44.0
+org.eclipse.platform:org.eclipse.core.commands:3.12.500
+org.eclipse.platform:org.eclipse.core.contenttype:3.9.800
+org.eclipse.platform:org.eclipse.core.expressions:3.9.500
+org.eclipse.platform:org.eclipse.core.filesystem:1.11.400
+org.eclipse.platform:org.eclipse.core.jobs:3.15.700
+org.eclipse.platform:org.eclipse.core.resources:3.23.100
+org.eclipse.platform:org.eclipse.core.runtime:3.34.100
+org.eclipse.platform:org.eclipse.equinox.app:1.7.500
+org.eclipse.platform:org.eclipse.equinox.common:3.20.300
+org.eclipse.platform:org.eclipse.equinox.preferences:3.12.100
+org.eclipse.platform:org.eclipse.equinox.registry:3.12.600
+org.eclipse.platform:org.eclipse.equinox.supplement:1.12.100
+org.eclipse.platform:org.eclipse.osgi:3.24.0
+org.eclipse.platform:org.eclipse.text:3.14.500
+org.osgi:org.osgi.service.prefs:1.1.2
diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.39.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.39.lockfile
new file mode 100644
index 0000000000..e86e95e3d3
--- /dev/null
+++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.39.lockfile
@@ -0,0 +1,19 @@
+# Spotless formatter based on Eclipse-JDT 4.39
+org.eclipse.jdt:org.eclipse.jdt.core:3.45.0
+net.java.dev.jna:jna-platform:5.18.1
+org.eclipse.jdt:ecj:3.45.0
+org.eclipse.platform:org.eclipse.core.commands:3.12.500
+org.eclipse.platform:org.eclipse.core.contenttype:3.9.800
+org.eclipse.platform:org.eclipse.core.expressions:3.9.500
+org.eclipse.platform:org.eclipse.core.filesystem:1.11.400
+org.eclipse.platform:org.eclipse.core.jobs:3.15.700
+org.eclipse.platform:org.eclipse.core.resources:3.23.200
+org.eclipse.platform:org.eclipse.core.runtime:3.34.200
+org.eclipse.platform:org.eclipse.equinox.app:1.7.600
+org.eclipse.platform:org.eclipse.equinox.common:3.20.300
+org.eclipse.platform:org.eclipse.equinox.preferences:3.12.100
+org.eclipse.platform:org.eclipse.equinox.registry:3.12.600
+org.eclipse.platform:org.eclipse.equinox.supplement:1.12.200
+org.eclipse.platform:org.eclipse.osgi:3.24.100
+org.eclipse.platform:org.eclipse.text:3.14.600
+org.osgi:org.osgi.service.prefs:1.1.2
diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.40.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.40.lockfile
new file mode 100644
index 0000000000..f30d63dc8c
--- /dev/null
+++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.40.lockfile
@@ -0,0 +1,19 @@
+# Spotless formatter based on Eclipse-JDT 4.40
+org.eclipse.jdt:org.eclipse.jdt.core:3.46.0
+net.java.dev.jna:jna-platform:5.18.1
+org.eclipse.jdt:ecj:3.46.0
+org.eclipse.platform:org.eclipse.core.commands:3.12.500
+org.eclipse.platform:org.eclipse.core.contenttype:3.9.800
+org.eclipse.platform:org.eclipse.core.expressions:3.9.600
+org.eclipse.platform:org.eclipse.core.filesystem:1.11.400
+org.eclipse.platform:org.eclipse.core.jobs:3.15.800
+org.eclipse.platform:org.eclipse.core.resources:3.24.0
+org.eclipse.platform:org.eclipse.core.runtime:3.34.200
+org.eclipse.platform:org.eclipse.equinox.app:1.7.600
+org.eclipse.platform:org.eclipse.equinox.common:3.20.400
+org.eclipse.platform:org.eclipse.equinox.preferences:3.12.100
+org.eclipse.platform:org.eclipse.equinox.registry:3.12.600
+org.eclipse.platform:org.eclipse.equinox.supplement:1.12.300
+org.eclipse.platform:org.eclipse.osgi:3.24.200
+org.eclipse.platform:org.eclipse.text:3.14.700
+org.osgi:org.osgi.service.prefs:1.1.2
diff --git a/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.9.lockfile b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.9.lockfile
new file mode 100644
index 0000000000..19ddfb2389
--- /dev/null
+++ b/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_jdt_formatter/v4.9.lockfile
@@ -0,0 +1,16 @@
+# Spotless formatter based on Eclipse-JDT 4.9
+org.eclipse.jdt:org.eclipse.jdt.core:3.15.0
+org.eclipse.platform:org.eclipse.core.commands:3.9.200
+org.eclipse.platform:org.eclipse.core.contenttype:3.7.100
+org.eclipse.platform:org.eclipse.core.expressions:3.6.200
+org.eclipse.platform:org.eclipse.core.filesystem:1.7.200
+org.eclipse.platform:org.eclipse.core.jobs:3.10.100
+org.eclipse.platform:org.eclipse.core.resources:3.13.100
+org.eclipse.platform:org.eclipse.core.runtime:3.15.0
+org.eclipse.platform:org.eclipse.equinox.app:1.3.600
+org.eclipse.platform:org.eclipse.equinox.common:3.10.100
+org.eclipse.platform:org.eclipse.equinox.preferences:3.7.200
+org.eclipse.platform:org.eclipse.equinox.registry:3.8.100
+org.eclipse.platform:org.eclipse.equinox.supplement:1.8.100
+org.eclipse.platform:org.eclipse.osgi:3.13.100
+org.eclipse.platform:org.eclipse.text:3.7.0
diff --git a/lib-extra/src/test/java/com/diffplug/spotless/extra/EquoBasedStepBuilderLockfileTest.java b/lib-extra/src/test/java/com/diffplug/spotless/extra/EquoBasedStepBuilderLockfileTest.java
new file mode 100644
index 0000000000..b6d859e559
--- /dev/null
+++ b/lib-extra/src/test/java/com/diffplug/spotless/extra/EquoBasedStepBuilderLockfileTest.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2016-2026 DiffPlug
+ *
+ * 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
+ *
+ * http://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.diffplug.spotless.extra;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyCollection;
+import static org.mockito.ArgumentMatchers.assertArg;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.when;
+
+import java.io.File;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+import com.diffplug.common.collect.ImmutableMap;
+import com.diffplug.spotless.Provisioner;
+import com.diffplug.spotless.StepHarness;
+
+import dev.equo.solstice.p2.P2Model;
+
+class EquoBasedStepBuilderLockfileTest {
+
+ @Test
+ void missingEmbeddedLockfileFallsBackToP2() throws Exception {
+ P2Provisioner p2Provisioner = mock();
+ Provisioner mavenProvisioner = mock();
+ when(p2Provisioner.provisionP2Dependencies(any(), any(), any())).thenReturn(List.of());
+ EquoBasedStepBuilder builder = builderWithLockfilePath("/com/diffplug/spotless/extra/missing.lockfile", p2Provisioner, mavenProvisioner);
+ StepHarness.forStep(builder.build()).test("class T {}", "class T {}");
+ verify(p2Provisioner).provisionP2Dependencies(any(), any(), any());
+ verifyNoInteractions(mavenProvisioner);
+ }
+
+ @Test
+ void lockfilePathMustBeAbsolute() {
+ P2Provisioner p2Provisioner = mock();
+ Provisioner mavenProvisioner = mock();
+ EquoBasedStepBuilder builder = builderWithLockfilePath("com/diffplug/spotless/extra/empty.lockfile",
+ p2Provisioner, mavenProvisioner);
+ IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
+ () -> StepHarness.forStep(builder.build()).test("class T {}", "class T {}"));
+ assertTrue(exception.getMessage().contains("must start with '/'"));
+ verifyNoInteractions(p2Provisioner, mavenProvisioner);
+ }
+
+ @Test
+ void emptyEmbeddedLockfileThrows() {
+ P2Provisioner p2Provisioner = mock();
+ Provisioner mavenProvisioner = mock();
+ EquoBasedStepBuilder builder = builderWithLockfilePath("/com/diffplug/spotless/extra/empty.lockfile",
+ p2Provisioner, mavenProvisioner);
+ IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
+ () -> StepHarness.forStep(builder.build()).test("class T {}", "class T {}"));
+ assertTrue(exception.getMessage().contains("No dependencies defined in lockfile"));
+ verifyNoInteractions(p2Provisioner, mavenProvisioner);
+ }
+
+ @Test
+ void embeddedLockfileUsesWithoutTransitivesAndAllCoordinates(@TempDir Path tempDir) throws Exception {
+ P2Provisioner p2Provisioner = mock();
+ Provisioner mavenProvisioner = mock();
+ File dummyJar = tempDir.resolve("spotless-lockfile-test.jar").toFile();
+ assertTrue(dummyJar.createNewFile());
+ when(mavenProvisioner.provisionWithTransitives(eq(false), anyCollection())).thenReturn(Set.of(dummyJar));
+ EquoBasedStepBuilder builder = builderWithLockfilePath("/com/diffplug/spotless/extra/two-deps.lockfile",
+ p2Provisioner, mavenProvisioner);
+
+ StepHarness.forStep(builder.build()).test("class T {}", "class T {}");
+
+ verify(mavenProvisioner).provisionWithTransitives(eq(false), assertArg((Collection coords) -> assertThat(coords)
+ .containsExactlyInAnyOrder(
+ "org.eclipse.jdt:org.eclipse.jdt.core:3.45.0",
+ "org.eclipse.jdt:ecj:3.45.0")));
+ verifyNoInteractions(p2Provisioner);
+ }
+
+ private static EquoBasedStepBuilder builderWithLockfilePath(String lockfilePath, P2Provisioner p2Provisioner, Provisioner mavenProvisioner) {
+ return new EquoBasedStepBuilder(
+ "lockfile test formatter",
+ mavenProvisioner,
+ p2Provisioner,
+ "4.40",
+ state -> input -> input,
+ ImmutableMap.builder()) {
+ @Override
+ protected P2Model model(String version) {
+ return new P2Model();
+ }
+
+ @Override
+ protected String lockfileResourcePath(String version) {
+ return lockfilePath;
+ }
+ };
+ }
+
+}
diff --git a/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStepTest.java b/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStepTest.java
index ff8eeaeae7..1ad8066f98 100644
--- a/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStepTest.java
+++ b/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtFormatterStepTest.java
@@ -15,19 +15,64 @@
*/
package com.diffplug.spotless.extra.java;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verifyNoInteractions;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
import java.util.stream.Stream;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.MethodSource;
+import org.junit.jupiter.params.provider.FieldSource;
+import com.diffplug.spotless.StepHarnessWithFile;
import com.diffplug.spotless.TestP2Provisioner;
import com.diffplug.spotless.TestProvisioner;
import com.diffplug.spotless.extra.EquoBasedStepBuilder;
+import com.diffplug.spotless.extra.P2Provisioner;
import com.diffplug.spotless.extra.eclipse.EquoResourceHarness;
class EclipseJdtFormatterStepTest extends EquoResourceHarness {
+
+ private static final String LOCKFILE_SUFFIX = ".lockfile";
+
+ /**
+ * Every supported version ships a lockfile, but actually formatting with one downloads that
+ * release's jars, so the round-trip tests below run against a representative sample rather than
+ * the whole range. The sample spans both of Eclipse's Maven dependency styles:
+ *
+ * - Range-based Maven POM dependencies: 4.9, 4.11, and 4.25
+ * - Exact Maven POM dependencies: 4.26, 4.39, and the default version
+ *
+ * The styles changed in
+ * eclipse-platform/eclipse.platform.releng#135,
+ * effective 4.26, which switched Maven dependency mapping from OSGi ranges to resolved concrete
+ * versions. It is a sampling boundary only -- it does not affect which versions ship a lockfile.
+ */
+ private static final List SAMPLED_LOCKFILE_VERSIONS = List.of("4.9", "4.11", "4.25", "4.26", "4.39", EclipseJdtFormatterStep.defaultVersion());
+
+ /**
+ * The shipped lockfiles are exactly the supported range -- no gaps, no strays. Contiguity is
+ * what makes the P2 fallback predictable: which resolution path you get should not depend on
+ * which release you happened to pin. See {@link EclipseJdtLockfileMetadataTool}, and run
+ * {@code ./gradlew :lib-extra:updateEclipseJdtLockfiles} when the default version moves.
+ */
+ @Test
+ void shippedLockfilesAreExactlyTheSupportedRange() throws IOException {
+ try (Stream files = Files.list(EclipseJdtLockfileMetadataTool.lockfileDir())) {
+ List shipped = files.map(path -> path.getFileName().toString())
+ .filter(name -> name.endsWith(LOCKFILE_SUFFIX))
+ .map(name -> name.substring(1, name.length() - LOCKFILE_SUFFIX.length()))
+ .toList();
+ assertThat(shipped).containsExactlyInAnyOrderElementsOf(EclipseJdtLockfileMetadataTool.targetVersions());
+ }
+ }
+
private static EquoBasedStepBuilder createBuilder() {
return EclipseJdtFormatterStep.createBuilder(TestProvisioner.mavenCentral(), TestP2Provisioner.defaultProvisioner());
}
@@ -37,15 +82,24 @@ public EclipseJdtFormatterStepTest() {
}
@ParameterizedTest
- @MethodSource
+ @FieldSource("SAMPLED_LOCKFILE_VERSIONS")
void formatWithVersion(String version) throws Exception {
harnessFor(version).test("test.java",
"package p; class C{}",
"package p;\nclass C {\n}");
}
- private static Stream formatWithVersion() {
- return Stream.of("4.9", EclipseJdtFormatterStep.defaultVersion());
+ @ParameterizedTest
+ @FieldSource("SAMPLED_LOCKFILE_VERSIONS")
+ void sampledLockfileVersionsDoNotUseP2(String version) {
+ P2Provisioner p2Provisioner = mock();
+ EclipseJdtFormatterStep.Builder builder = EclipseJdtFormatterStep.createBuilder(TestProvisioner.mavenCentral(), p2Provisioner);
+ builder.setVersion(version);
+ StepHarnessWithFile.forStep(this, builder.build()).test(
+ "test.java",
+ "package p; class C{}",
+ "package p;\nclass C {\n}");
+ verifyNoInteractions(p2Provisioner);
}
/** New format interface requires source file information to distinguish module-info from compilation unit */
diff --git a/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtLockfileMetadataTool.java b/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtLockfileMetadataTool.java
new file mode 100644
index 0000000000..bc4b4334bc
--- /dev/null
+++ b/lib-extra/src/test/java/com/diffplug/spotless/extra/java/EclipseJdtLockfileMetadataTool.java
@@ -0,0 +1,291 @@
+/*
+ * Copyright 2016-2026 DiffPlug
+ *
+ * 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
+ *
+ * http://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.diffplug.spotless.extra.java;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.StringJoiner;
+
+import dev.equo.solstice.p2.P2ClientCache;
+import dev.equo.solstice.p2.P2Model;
+import dev.equo.solstice.p2.P2QueryCache;
+import dev.equo.solstice.p2.P2QueryResult;
+
+/**
+ * Executable class for validating or updating embedded JDT lockfiles from Eclipse P2 metadata.
+ *
+ * Intended for manual execution via the following entrypoints:
+ *
+ * - {@link EclipseJdtLockfileMetadataTool.Verify#main(String[])}
+ * - {@link EclipseJdtLockfileMetadataTool.Update#main(String[])}
+ *
+ */
+public class EclipseJdtLockfileMetadataTool {
+
+ /**
+ * Every Eclipse release from {@value #OLDEST_MINOR}, inclusive, through the version Spotless
+ * defaults to gets an embedded lockfile, with no gaps.
+ *
+ * Contiguity is the point: a user on any supported version should get the same resolution path
+ * as a user on any other. Sampling versions instead would make the P2 fallback -- and the
+ * behavior differences that come with it -- depend on which release you happen to pin.
+ *
+ * Eclipse's switch from OSGi ranges to resolved concrete versions in its Maven metadata
+ * (eclipse-platform/eclipse.platform.releng#135,
+ * effective 4.26) deliberately does not factor in. Full explicit lockfiles can be
+ * produced for any target by taking Solstice's P2-resolved Maven coordinates directly, which
+ * are already fully version-resolved under either style.
+ */
+ private static final int OLDEST_MINOR = 9;
+
+ static List targetVersions() {
+ String defaultVersion = EclipseJdtFormatterStep.defaultVersion();
+ int newestMinor = minorOf(defaultVersion);
+ if (newestMinor < OLDEST_MINOR) {
+ throw new IllegalStateException("Default version " + defaultVersion + " is older than 4." + OLDEST_MINOR);
+ }
+ List versions = new ArrayList<>();
+ for (int minor = OLDEST_MINOR; minor <= newestMinor; minor++) {
+ versions.add("4." + minor);
+ }
+ return versions;
+ }
+
+ private static int minorOf(String version) {
+ if (!version.startsWith("4.")) {
+ throw new IllegalArgumentException("Expected 4.x but got " + version);
+ }
+ return Integer.parseInt(version.substring(2).split("\\.")[0]);
+ }
+
+ /**
+ * Verifies the JDT lockfiles at {@link #targetVersions()} against Eclipse P2 metadata.
+ *
+ * Exits non-zero if any lockfile is missing or out of date, so this can gate CI.
+ */
+ public static class Verify {
+
+ public static void main(String[] args) {
+ if (run(false) > 0) {
+ System.exit(1);
+ }
+ }
+
+ }
+
+ /**
+ * Updates the JDT lockfiles at {@link #targetVersions()} from Eclipse P2 metadata.
+ *
+ * Missing lockfiles will be created.
+ */
+ public static class Update {
+
+ public static void main(String[] args) {
+ if (run(true) > 0) {
+ System.exit(1);
+ }
+ }
+
+ }
+
+ private static final String JDT_ID = "org.eclipse.jdt.core";
+ private static final String JDT_MAVEN_KEY = "org.eclipse.jdt:org.eclipse.jdt.core";
+ private static final List ECLIPSE_UPDATE_BASE_URLS = List.of(
+ "https://download.eclipse.org/eclipse/updates/",
+ "https://archive.eclipse.org/eclipse/updates/");
+
+ /** Returns the number of failures. */
+ private static int run(boolean update) {
+ Path lockfileDir = lockfileDir();
+ Map lockfilesByVersion = targetLockfiles(lockfileDir, targetVersions());
+
+ int checked = 0;
+ int failures = 0;
+ for (Map.Entry entry : lockfilesByVersion.entrySet()) {
+ checked++;
+ String eclipseVersion = entry.getKey();
+ Path lockfilePath = entry.getValue();
+ try {
+ List allCoordinates = resolveTransitiveClosure(eclipseVersion);
+ String rootCoordinate = allCoordinates.get(0);
+ if (update) {
+ Files.writeString(lockfilePath, lockfileContent(eclipseVersion, allCoordinates), StandardCharsets.UTF_8);
+ System.out.println("WROTE v" + eclipseVersion + " -> " + allCoordinates.size() + " artifact(s), root: " + rootCoordinate);
+ } else {
+ if (!Files.exists(lockfilePath)) {
+ System.err.println("MISSING v" + eclipseVersion + " -> " + lockfilePath + " (expected root: " + rootCoordinate + ")");
+ failures++;
+ continue;
+ }
+ List actualCoordinates = lockfileCoordinates(lockfilePath);
+ String mismatch = mismatchMessage(eclipseVersion, allCoordinates, actualCoordinates);
+ if (mismatch != null) {
+ System.err.println(mismatch);
+ failures++;
+ continue;
+ }
+ System.out.println("OK v" + eclipseVersion + " -> " + rootCoordinate + " (" + allCoordinates.size() + " coordinates)");
+ }
+ } catch (Exception e) {
+ System.err.println("ERROR v" + eclipseVersion + " -> " + e.getMessage());
+ failures++;
+ }
+ }
+ if (update) {
+ System.out.println("Updated " + checked + " lockfile(s) with " + failures + " issue(s).");
+ } else {
+ System.out.println("Verified " + checked + " lockfile(s) with " + failures + " issue(s).");
+ }
+ return failures;
+ }
+
+ private static Map targetLockfiles(Path lockfileDir, List versions) {
+ Map targets = new LinkedHashMap<>();
+ for (String version : versions) {
+ targets.put(version, lockfileDir.resolve("v" + version + ".lockfile"));
+ }
+ return targets;
+ }
+
+ private static String lockfileContent(String eclipseVersion, List coordinates) {
+ String prefix = "# Spotless formatter based on Eclipse-JDT " + eclipseVersion + "\n";
+ StringJoiner joiner = new StringJoiner("\n", prefix, "\n");
+ coordinates.forEach(joiner::add);
+ return joiner.toString();
+ }
+
+ private static List lockfileCoordinates(Path lockfilePath) throws IOException {
+ try (var lines = Files.lines(lockfilePath, StandardCharsets.UTF_8)) {
+ List coordinates = lines.map(String::trim)
+ .filter(line -> !line.isEmpty())
+ .filter(line -> !line.startsWith("#"))
+ .toList();
+ if (coordinates.isEmpty()) {
+ throw new IllegalArgumentException("No dependency coordinate found in " + lockfilePath);
+ }
+ return coordinates;
+ }
+ }
+
+ static String mismatchMessage(String eclipseVersion, List expectedCoordinates, List actualCoordinates) {
+ if (expectedCoordinates.equals(actualCoordinates)) {
+ return null;
+ }
+ String expectedRoot = expectedCoordinates.isEmpty() ? "" : expectedCoordinates.get(0);
+ String actualRoot = actualCoordinates.isEmpty() ? "" : actualCoordinates.get(0);
+ return "MISMATCH v" + eclipseVersion + " -> expected root: " + expectedRoot + " (" + expectedCoordinates.size()
+ + " coordinates), actual: " + actualRoot + " (" + actualCoordinates.size() + " coordinates)";
+ }
+
+ static Path lockfileDir() {
+ Path fromRepoRoot = Path.of("lib-extra", "src", "main", "resources", "com", "diffplug", "spotless", "extra", "eclipse_jdt_formatter");
+ if (Files.isDirectory(fromRepoRoot)) {
+ return fromRepoRoot;
+ }
+ Path fromLibExtra = Path.of("src", "main", "resources", "com", "diffplug", "spotless", "extra", "eclipse_jdt_formatter");
+ if (Files.isDirectory(fromLibExtra)) {
+ return fromLibExtra;
+ }
+ throw new IllegalStateException("Unable to locate eclipse_jdt_formatter resource directory");
+ }
+
+ /**
+ * Resolves full, explicit Maven coordinates from the P2 query result for the given Eclipse version.
+ *
+ * This supports both exact and range-based historical metadata because the P2 solver has already
+ * chosen concrete versions before exposing Maven coordinates.
+ */
+ private static List resolveTransitiveClosure(String eclipseVersion) throws Exception {
+ P2QueryResult query = queryJdtFromP2(eclipseVersion);
+
+ // "g:a" -> selected version
+ Map selected = new LinkedHashMap<>();
+ for (String coordinate : query.getJarsOnMavenCentral()) {
+ String[] parts = coordinate.split(":");
+ if (parts.length != 3) {
+ throw new IllegalStateException("Expected Maven coordinate g:a:v but got: " + coordinate);
+ }
+ String groupId = parts[0], artifactId = parts[1], version = parts[2];
+ String key = groupId + ":" + artifactId;
+ String existing = selected.get(key);
+ if (existing == null || compareVersions(existing, version) < 0) {
+ selected.put(key, version);
+ }
+ }
+ String rootVersion = selected.remove(JDT_MAVEN_KEY);
+ if (rootVersion == null) {
+ throw new IllegalStateException("P2 result for Eclipse " + eclipseVersion + " did not contain " + JDT_MAVEN_KEY);
+ }
+ List result = new ArrayList<>();
+ result.add(JDT_MAVEN_KEY + ":" + rootVersion);
+ selected.entrySet().stream()
+ .map(e -> e.getKey() + ":" + e.getValue())
+ .sorted()
+ .forEach(result::add);
+ return result;
+ }
+
+ private static P2QueryResult queryJdtFromP2(String eclipseVersion) throws Exception {
+ Exception lastError = null;
+ for (String baseUrl : ECLIPSE_UPDATE_BASE_URLS) {
+ try {
+ P2Model model = new P2Model();
+ addPlatformRepo(model, eclipseVersion, baseUrl);
+ model.getInstall().add(JDT_ID);
+ return model.query(P2ClientCache.PREFER_OFFLINE, P2QueryCache.ALLOW);
+ } catch (Exception e) {
+ lastError = e;
+ }
+ }
+ throw new IllegalStateException("Failed to query Eclipse " + eclipseVersion + " from known update sites", lastError);
+ }
+
+ private static void addPlatformRepo(P2Model model, String version, String baseUrl) {
+ if (!version.startsWith("4.")) {
+ throw new IllegalArgumentException("Expected 4.x");
+ }
+ model.addP2Repo(baseUrl + version + "/");
+ }
+
+ /** Compares two version strings numerically, segment by segment. Returns positive if v1 > v2. */
+ private static int compareVersions(String v1, String v2) {
+ String[] p1 = v1.split("\\.");
+ String[] p2 = v2.split("\\.");
+ int len = Math.max(p1.length, p2.length);
+ for (int i = 0; i < len; i++) {
+ int n1 = i < p1.length ? parseVersionSegment(p1[i]) : 0;
+ int n2 = i < p2.length ? parseVersionSegment(p2[i]) : 0;
+ if (n1 != n2)
+ return Integer.compare(n1, n2);
+ }
+ return 0;
+ }
+
+ private static int parseVersionSegment(String segment) {
+ try {
+ return Integer.parseInt(segment);
+ } catch (NumberFormatException e) {
+ return 0;
+ }
+ }
+
+}
diff --git a/lib-extra/src/test/resources/com/diffplug/spotless/extra/empty.lockfile b/lib-extra/src/test/resources/com/diffplug/spotless/extra/empty.lockfile
new file mode 100644
index 0000000000..d25c36fa9a
--- /dev/null
+++ b/lib-extra/src/test/resources/com/diffplug/spotless/extra/empty.lockfile
@@ -0,0 +1 @@
+# intentionally empty lockfile used by EquoBasedStepBuilderLockfileTest
diff --git a/lib-extra/src/test/resources/com/diffplug/spotless/extra/two-deps.lockfile b/lib-extra/src/test/resources/com/diffplug/spotless/extra/two-deps.lockfile
new file mode 100644
index 0000000000..757276a5bb
--- /dev/null
+++ b/lib-extra/src/test/resources/com/diffplug/spotless/extra/two-deps.lockfile
@@ -0,0 +1,3 @@
+# test lockfile
+org.eclipse.jdt:org.eclipse.jdt.core:3.45.0
+org.eclipse.jdt:ecj:3.45.0
diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md
index 169371d109..e500f61fa3 100644
--- a/plugin-gradle/CHANGES.md
+++ b/plugin-gradle/CHANGES.md
@@ -7,6 +7,8 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
- `spotlessCheck` violation message now suggests the correct composite/included-build task path (e.g. `./gradlew :my-utils:spotlessApply`) instead of a bare `spotlessApply` / `:spotlessApply` that does not select included-build tasks. ([#2421](https://github.com/diffplug/spotless/issues/2421))
- Parallel multi-project builds no longer intermittently fail with "Cannot fingerprint input property 'stepsInternalEquality': ConfigurationCacheHackList cannot be serialized" / "Failed to provision P2 dependencies" when using `eclipse()` (or other P2-backed steps). Subprojects now share one deduping P2 provisioner and P2 queries are serialized process-wide. ([#3004](https://github.com/diffplug/spotless/issues/3004))
### Changes
+- Add embedded lockfiles to Eclipse JDT for every supported version (`4.9` through `4.40`), so `eclipse()` resolves from Maven Central instead of querying a P2 update site. Versions without an embedded lockfile still fall back to P2 provisioning. ([#1996](https://github.com/diffplug/spotless/issues/1996))
+- Bump default `eclipse` version to latest `4.39` -> `4.40`. ([#1996](https://github.com/diffplug/spotless/issues/1996))
- Bump default `adocfmt` version `0.2.0` -> `0.3.1`, which adds table formatting support (`formatTables`, `tableLayout`, `tableMaxLineWidth`, `tableBlankLines`).
## [8.9.0] - 2026-07-27
diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/SpotlessPredeclareIntegrationTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/SpotlessPredeclareIntegrationTest.java
index 796c8896ec..5f54de218d 100644
--- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/SpotlessPredeclareIntegrationTest.java
+++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/SpotlessPredeclareIntegrationTest.java
@@ -209,9 +209,13 @@ target file('test.java')
setFile("sub/test.java").toResource("java/eclipse/JavaCodeUnformatted.test");
BuildResult result = gradleRunner().withArguments("spotlessApply").buildAndFail();
+ // Eclipse JDT resolves from an embedded lockfile rather than a P2 query, so an
+ // un-predeclared subproject fails on the Maven coordinates instead of on P2.
+ // greclipse still has no lockfile, so it keeps the P2 message - see
+ // GroovyDependencies.predeclareFailsWhenGroovyGrEclipseNotPredeclared.
assertThat(result.getOutput())
- .contains("P2 dependencies not predeclared")
- .contains("Add Eclipse formatter configuration to the `spotlessPredeclare` block in the root project");
+ .contains("into the `spotlessPredeclare` block in the root project")
+ .contains("org.eclipse.jdt:org.eclipse.jdt.core");
}
@Test
diff --git a/plugin-maven/CHANGES.md b/plugin-maven/CHANGES.md
index e82e3cfab1..e5a29e7867 100644
--- a/plugin-maven/CHANGES.md
+++ b/plugin-maven/CHANGES.md
@@ -6,6 +6,8 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
### Fixed
- Concurrent P2 provisioning no longer races Solstice's on-disk cache (affects Eclipse-based formatters under parallel builds). ([#3004](https://github.com/diffplug/spotless/issues/3004))
### Changes
+- Add embedded lockfiles to Eclipse JDT for every supported version (`4.9` through `4.40`), so `eclipse()` resolves from Maven Central instead of querying a P2 update site. Versions without an embedded lockfile still fall back to P2 provisioning. ([#1996](https://github.com/diffplug/spotless/issues/1996))
+- Bump default `eclipse` version to latest `4.39` -> `4.40`. ([#1996](https://github.com/diffplug/spotless/issues/1996))
- Document Maven skip properties `spotless.skip`, `spotless.check.skip`, and `spotless.apply.skip`. Goal-specific skips now live on their own mojos so they no longer leak across goals. ([#3009](https://github.com/diffplug/spotless/pull/3009))
- Bump default `adocfmt` version `0.2.0` -> `0.3.1`, which adds table formatting support (``, ``, ``, ``).
- Add support to apply alternate license header within same format ([#872](https://github.com/diffplug/spotless/issues/872))
diff --git a/plugin-maven/src/test/java/com/diffplug/spotless/maven/FormatterStepFactoryTest.java b/plugin-maven/src/test/java/com/diffplug/spotless/maven/FormatterStepFactoryTest.java
index ff98996431..f2b5a2f31c 100644
--- a/plugin-maven/src/test/java/com/diffplug/spotless/maven/FormatterStepFactoryTest.java
+++ b/plugin-maven/src/test/java/com/diffplug/spotless/maven/FormatterStepFactoryTest.java
@@ -24,9 +24,11 @@
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.aether.RepositorySystemSession;
@@ -51,11 +53,6 @@ void defaultP2CacheDirectoryUsesMavenLocalRepository() {
.isEqualTo(new File(localRepository, "dev/equo/p2-data"));
}
- @Test
- void eclipseUsesConfiguredCacheDirectory() throws Exception {
- assertUsesConfiguredCacheDirectory(new Eclipse());
- }
-
@Test
void grEclipseUsesConfiguredCacheDirectory() throws Exception {
assertUsesConfiguredCacheDirectory(new GrEclipse());
@@ -66,11 +63,6 @@ void eclipseCdtUsesConfiguredCacheDirectory() throws Exception {
assertUsesConfiguredCacheDirectory(new EclipseCdt());
}
- @Test
- void eclipseUsesDefaultCacheDirectory() throws Exception {
- assertUsesDefaultCacheDirectory(new Eclipse());
- }
-
@Test
void grEclipseUsesDefaultCacheDirectory() throws Exception {
assertUsesDefaultCacheDirectory(new GrEclipse());
@@ -81,6 +73,44 @@ void eclipseCdtUsesDefaultCacheDirectory() throws Exception {
assertUsesDefaultCacheDirectory(new EclipseCdt());
}
+ /**
+ * Eclipse JDT ships an embedded lockfile for its default version, so it resolves straight from
+ * Maven and never runs a P2 query -- which also means its P2 cache directory goes unused. The
+ * P2 cache directory behavior is covered by the grEclipse and eclipseCdt cases above, neither
+ * of which has an embedded lockfile.
+ */
+ @Test
+ void eclipseResolvesFromEmbeddedLockfileInsteadOfP2() throws Exception {
+ Eclipse factory = new Eclipse();
+ factory.init(repositorySystemSession(tempDir.resolve("local-repo").toFile()));
+
+ AtomicReference> actualCoordinates = new AtomicReference<>();
+ AtomicBoolean p2WasQueried = new AtomicBoolean();
+ File fakeJar = tempDir.resolve("fake.jar").toFile();
+ Files.write(fakeJar.toPath(), new byte[]{0});
+
+ FormatterStep step = factory.newFormatterStep(new FormatterStepConfig(
+ UTF_8,
+ "",
+ Optional.empty(),
+ (withTransitives, mavenCoordinates) -> {
+ actualCoordinates.set(mavenCoordinates);
+ return Set.of(fakeJar);
+ },
+ (modelWrapper, mavenProvisioner, cacheDirectory) -> {
+ p2WasQueried.set(true);
+ return List.of(fakeJar);
+ },
+ null,
+ Optional.empty(),
+ Optional.empty()));
+
+ int unused = step.hashCode();
+
+ assertThat(p2WasQueried).isFalse();
+ assertThat(actualCoordinates.get()).anyMatch(coordinate -> coordinate.startsWith("org.eclipse.jdt:org.eclipse.jdt.core:"));
+ }
+
private void assertUsesConfiguredCacheDirectory(FormatterStepFactory factory) throws Exception {
File configuredCacheDirectory = tempDir.resolve("configured-p2-cache").toFile();
Field cacheDirectory = cacheDirectoryField(factory);