Skip to content

Commit da0ff27

Browse files
author
Ned Twigg
committed
Merge branch 'main' into g/20250702/gradle-9
2 parents 4c80f8e + 94da761 commit da0ff27

40 files changed

+1725
-152
lines changed

CHANGES.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,16 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
1111

1212
## [Unreleased]
1313

14+
## [3.3.0] - 2025-07-20
15+
### Added
16+
* Allow specifying path to Biome JSON config file directly in `biome` step. Requires biome 2.x. ([#2548](https://github.com/diffplug/spotless/pull/2548))
17+
* `GitPrePushHookInstaller`, a reusable library component for installing a Git `pre-push` hook that runs formatter checks. ([#2553](https://github.com/diffplug/spotless/pull/2553))
18+
* Allow setting Eclipse XML config from a string, not only from files ([#2361](https://github.com/diffplug/spotless/pull/2361))
1419
## Changed
1520
* Bump default `gson` version to latest `2.11.0` -> `2.13.1`. ([#2414](https://github.com/diffplug/spotless/pull/2414))
16-
* Bump default `jackson` version to latest `2.18.1` -> `2.19.1`. ([#2352](https://github.com/diffplug/spotless/pull/2352))
21+
* Bump default `jackson` version to latest `2.18.1` -> `2.19.2`. ([#2558](https://github.com/diffplug/spotless/pull/2558))
1722
* Bump default `gherkin-utils` version to latest `9.0.0` -> `9.2.0`. ([#2408](https://github.com/diffplug/spotless/pull/2408))
23+
* Bump default `cleanthat` version to latest `2.22` -> `2.23`. ([#2556](https://github.com/diffplug/spotless/pull/2556))
1824

1925
## [3.2.0] - 2025-07-07
2026
### Added

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ If you get something running, we'd love to host your plugin within this repo as
163163

164164
To run all tests, simply do
165165

166-
> gradlew test
166+
> ./gradlew test
167167
168168
Since that takes some time, you might only want to run the tests
169169
concerning what you are working on:

lib-extra/src/main/java/com/diffplug/spotless/extra/EquoBasedStepBuilder.java

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ public abstract class EquoBasedStepBuilder {
5656
private String formatterVersion;
5757
private Iterable<File> settingsFiles = new ArrayList<>();
5858
private List<String> settingProperties = new ArrayList<>();
59+
private List<String> settingXml = new ArrayList<>();
5960
private Map<String, String> p2Mirrors = Map.of();
6061
private File cacheDirectory;
6162

@@ -86,6 +87,10 @@ public void setPropertyPreferences(List<String> propertyPreferences) {
8687
this.settingProperties = propertyPreferences;
8788
}
8889

90+
public void setXmlPreferences(List<String> settingXml) {
91+
this.settingXml = settingXml;
92+
}
93+
8994
public void setP2Mirrors(Map<String, String> p2Mirrors) {
9095
this.p2Mirrors = Map.copyOf(p2Mirrors);
9196
}
@@ -119,7 +124,7 @@ protected void addPlatformRepo(P2Model model, String version) {
119124

120125
/** Returns the FormatterStep (whose state will be calculated lazily). */
121126
public FormatterStep build() {
122-
var roundtrippableState = new EquoStep(formatterVersion, settingProperties, FileSignature.promise(settingsFiles), JarState.promise(() -> {
127+
var roundtrippableState = new EquoStep(formatterVersion, settingProperties, settingXml, FileSignature.promise(settingsFiles), JarState.promise(() -> {
123128
P2QueryResult query;
124129
try {
125130
if (null != cacheDirectory) {
@@ -174,23 +179,26 @@ static class EquoStep implements Serializable {
174179
private final JarState.Promised jarPromise;
175180
private final ImmutableMap<String, String> stepProperties;
176181
private List<String> settingProperties;
182+
private List<String> settingXml;
177183

178184
EquoStep(
179185
String semanticVersion,
180186
List<String> settingProperties,
187+
List<String> settingXml,
181188
FileSignature.Promised settingsPromise,
182189
JarState.Promised jarPromise,
183190
ImmutableMap<String, String> stepProperties) {
184191

185192
this.semanticVersion = semanticVersion;
186193
this.settingProperties = Optional.ofNullable(settingProperties).orElse(new ArrayList<>());
194+
this.settingXml = Optional.ofNullable(settingXml).orElse(new ArrayList<>());
187195
this.settingsPromise = settingsPromise;
188196
this.jarPromise = jarPromise;
189197
this.stepProperties = stepProperties;
190198
}
191199

192200
private State state() {
193-
return new State(semanticVersion, jarPromise.get(), settingProperties, settingsPromise.get(), stepProperties);
201+
return new State(semanticVersion, jarPromise.get(), settingProperties, settingXml, settingsPromise.get(), stepProperties);
194202
}
195203
}
196204

@@ -205,11 +213,13 @@ public static class State implements Serializable {
205213
final FileSignature settingsFiles;
206214
final ImmutableMap<String, String> stepProperties;
207215
private List<String> settingProperties;
216+
private List<String> settingXml;
208217

209-
public State(String semanticVersion, JarState jarState, List<String> settingProperties, FileSignature settingsFiles, ImmutableMap<String, String> stepProperties) {
218+
public State(String semanticVersion, JarState jarState, List<String> settingProperties, List<String> settingXml, FileSignature settingsFiles, ImmutableMap<String, String> stepProperties) {
210219
this.semanticVersion = semanticVersion;
211220
this.jarState = jarState;
212221
this.settingProperties = Optional.ofNullable(settingProperties).orElse(new ArrayList<>());
222+
this.settingXml = Optional.ofNullable(settingXml).orElse(new ArrayList<>());
213223
this.settingsFiles = settingsFiles;
214224
this.stepProperties = stepProperties;
215225
}
@@ -225,7 +235,8 @@ public String getSemanticVersion() {
225235
public Properties getPreferences() {
226236
FormatterProperties fromFiles = FormatterProperties.from(settingsFiles.files());
227237
FormatterProperties fromPropertiesContent = FormatterProperties.fromPropertiesContent(settingProperties);
228-
return FormatterProperties.merge(fromFiles.getProperties(), fromPropertiesContent.getProperties()).getProperties();
238+
FormatterProperties fromXmlContent = FormatterProperties.fromXmlContent(settingXml);
239+
return FormatterProperties.merge(fromFiles.getProperties(), fromPropertiesContent.getProperties(), fromXmlContent.getProperties()).getProperties();
229240
}
230241

231242
public ImmutableMap<String, String> getStepProperties() {

lib/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ dependencies {
8383

8484
// GLUE CODE (alphabetic order please)
8585
// cleanthat
86-
String VER_CLEANTHAT='2.22'
86+
String VER_CLEANTHAT='2.23'
8787
cleanthatCompileOnly "io.github.solven-eu.cleanthat:java:$VER_CLEANTHAT"
8888
compatCleanthat2Dot1CompileAndTestOnly "io.github.solven-eu.cleanthat:java:$VER_CLEANTHAT"
8989
// diktat old supported version 1.x
@@ -100,7 +100,7 @@ dependencies {
100100
// gson
101101
gsonCompileOnly 'com.google.code.gson:gson:2.13.1'
102102
// jackson
103-
String VER_JACKSON='2.19.1'
103+
String VER_JACKSON='2.19.2'
104104
jacksonCompileOnly "com.fasterxml.jackson.core:jackson-databind:$VER_JACKSON"
105105
jacksonCompileOnly "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:$VER_JACKSON"
106106
// ktfmt

lib/src/main/java/com/diffplug/spotless/FormatterProperties.java

Lines changed: 62 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2024 DiffPlug
2+
* Copyright 2016-2025 DiffPlug
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@
2020
import java.io.ByteArrayInputStream;
2121
import java.io.File;
2222
import java.io.FileInputStream;
23+
import java.io.FileNotFoundException;
2324
import java.io.IOException;
2425
import java.io.InputStream;
2526
import java.nio.charset.StandardCharsets;
@@ -28,6 +29,7 @@
2829
import java.util.List;
2930
import java.util.Objects;
3031
import java.util.Properties;
32+
import java.util.function.Supplier;
3133
import java.util.stream.Collectors;
3234
import java.util.stream.IntStream;
3335

@@ -90,6 +92,25 @@ public static FormatterProperties fromPropertiesContent(Iterable<String> content
9092
return properties;
9193
}
9294

95+
public static FormatterProperties fromXmlContent(final Iterable<String> content) throws IllegalArgumentException {
96+
final List<String> nonNullElements = toNullHostileList(content);
97+
final FormatterProperties properties = new FormatterProperties();
98+
nonNullElements.forEach(contentElement -> {
99+
try {
100+
final Properties newSettings = FileParser.XML.executeXmlContent(contentElement);
101+
properties.properties.putAll(newSettings);
102+
} catch (IOException | IllegalArgumentException exception) {
103+
String message = String.format("Failed to add preferences from XML:%n%s%n", contentElement);
104+
final String detailedMessage = exception.getMessage();
105+
if (null != detailedMessage) {
106+
message += String.format(" %s", detailedMessage);
107+
}
108+
throw new IllegalArgumentException(message, exception);
109+
}
110+
});
111+
return properties;
112+
}
113+
93114
public static FormatterProperties merge(Properties... properties) {
94115
FormatterProperties merged = new FormatterProperties();
95116
List.of(properties).stream().forEach((source) -> merged.properties.putAll(source));
@@ -139,20 +160,45 @@ protected Properties execute(final File file) throws IOException, IllegalArgumen
139160
}
140161
return properties;
141162
}
163+
164+
@Override
165+
protected Properties executeXmlContent(String content) throws IOException, IllegalArgumentException {
166+
throw new RuntimeException("Not implemented");
167+
}
142168
},
143169

144170
XML("xml") {
145171
@Override
146172
protected Properties execute(final File file) throws IOException, IllegalArgumentException {
147-
Node rootNode = getRootNode(file);
148-
String nodeName = rootNode.getNodeName();
149-
if (null == nodeName) {
150-
throw new IllegalArgumentException("XML document does not contain a root node.");
173+
return executeWithSupplier(() -> {
174+
try {
175+
return new FileInputStream(file);
176+
} catch (FileNotFoundException e) {
177+
throw new RuntimeException("File not found: " + file, e);
178+
}
179+
});
180+
}
181+
182+
@Override
183+
protected Properties executeXmlContent(String content) throws IOException, IllegalArgumentException {
184+
return executeWithSupplier(() -> new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)));
185+
}
186+
187+
private Properties executeWithSupplier(Supplier<InputStream> isSupplier) throws IOException, IllegalArgumentException {
188+
Node rootNode;
189+
try (InputStream input = isSupplier.get()) {
190+
rootNode = getRootNode(input);
191+
String nodeName = rootNode.getNodeName();
192+
if (null == nodeName) {
193+
throw new IllegalArgumentException("XML document does not contain a root node.");
194+
}
195+
}
196+
try (InputStream input = isSupplier.get()) {
197+
return XmlParser.parse(input, rootNode);
151198
}
152-
return XmlParser.parse(file, rootNode);
153199
}
154200

155-
private Node getRootNode(final File file) throws IOException, IllegalArgumentException {
201+
private Node getRootNode(final InputStream is) throws IOException, IllegalArgumentException {
156202
try {
157203
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
158204
/*
@@ -166,7 +212,7 @@ private Node getRootNode(final File file) throws IOException, IllegalArgumentExc
166212
*/
167213
dbf.setFeature(LOAD_EXTERNAL_DTD_PROP, false);
168214
DocumentBuilder db = dbf.newDocumentBuilder();
169-
return db.parse(file).getDocumentElement();
215+
return db.parse(is).getDocumentElement();
170216
} catch (SAXException | ParserConfigurationException e) {
171217
throw new IllegalArgumentException("File has no valid XML syntax.", e);
172218
}
@@ -186,6 +232,8 @@ private Node getRootNode(final File file) throws IOException, IllegalArgumentExc
186232

187233
protected abstract Properties execute(File file) throws IOException, IllegalArgumentException;
188234

235+
protected abstract Properties executeXmlContent(String content) throws IOException, IllegalArgumentException;
236+
189237
public static Properties parse(final File file) throws IOException, IllegalArgumentException {
190238
String fileNameExtension = getFileNameExtension(file);
191239
for (FileParser parser : FileParser.values()) {
@@ -211,19 +259,17 @@ private static String getFileNameExtension(File file) {
211259
private enum XmlParser {
212260
PROPERTIES("properties") {
213261
@Override
214-
protected Properties execute(final File xmlFile, final Node rootNode)
262+
protected Properties execute(final InputStream xmlFile, final Node rootNode)
215263
throws IOException, IllegalArgumentException {
216264
final Properties properties = new Properties();
217-
try (InputStream xmlInput = new FileInputStream(xmlFile)) {
218-
properties.loadFromXML(xmlInput);
219-
}
265+
properties.loadFromXML(xmlFile);
220266
return properties;
221267
}
222268
},
223269

224270
PROFILES("profiles") {
225271
@Override
226-
protected Properties execute(File file, Node rootNode) throws IOException, IllegalArgumentException {
272+
protected Properties execute(InputStream file, Node rootNode) throws IOException, IllegalArgumentException {
227273
final Properties properties = new Properties();
228274
Node firstProfile = getSingleProfile(rootNode);
229275
for (Object settingObj : getChildren(firstProfile, "setting")) {
@@ -285,14 +331,14 @@ public String toString() {
285331
return this.rootNodeName;
286332
}
287333

288-
protected abstract Properties execute(File file, Node rootNode) throws IOException, IllegalArgumentException;
334+
protected abstract Properties execute(InputStream is, Node rootNode) throws IOException, IllegalArgumentException;
289335

290-
public static Properties parse(final File file, final Node rootNode)
336+
public static Properties parse(final InputStream is, final Node rootNode)
291337
throws IOException, IllegalArgumentException {
292338
String rootNodeName = rootNode.getNodeName();
293339
for (XmlParser parser : XmlParser.values()) {
294340
if (parser.rootNodeName.equals(rootNodeName)) {
295-
return parser.execute(file, rootNode);
341+
return parser.execute(is, rootNode);
296342
}
297343
}
298344
String msg = String.format("The XML root node '%1$s' is not part of the supported root nodes [%2$s].",

0 commit comments

Comments
 (0)