Skip to content

Commit 793638d

Browse files
authored
Replace de.atextor:turtle-formatter with cool.rdf:cool-rdf-formatter (#2995)
2 parents d407109 + 3cb7227 commit 793638d

16 files changed

Lines changed: 144 additions & 46 deletions

File tree

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
1313
### Added
1414
- Add support for Java formatting via [`prince-of-space`](https://github.com/agustafson/prince-of-space). ([#2991](https://github.com/diffplug/spotless/pull/2991))
1515
### Changes
16+
- Replace RDF formatter library `de.atextor:turtle-formatter` (discontinued) with `cool.rdf:cool-rdf-formatter` (its new coordinates); the RDF/turtle formatter now requires Java 25+. ([#2995](https://github.com/diffplug/spotless/pull/2995))
1617
- Bump default `greclipse` version to latest `4.39` -> `4.40`. ([#2989](https://github.com/diffplug/spotless/pull/2989))
1718
- Bump default `tabletest-formatter` version `1.1.1` -> `1.1.2`.
1819

gradle/special-tests.gradle

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,3 @@ special.forEach { tag ->
4545
}
4646
}
4747
}
48-

lib/src/main/java/com/diffplug/spotless/rdf/RdfFormatterConfig.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2024-2025 DiffPlug
2+
* Copyright 2024-2026 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.
@@ -86,6 +86,9 @@ public Builder verify() {
8686
}
8787

8888
public RdfFormatterConfig build() {
89+
if (config.turtleFormatterVersion == null) {
90+
config.turtleFormatterVersion = RdfFormatterStep.getDefaultFormatterVersion();
91+
}
8992
return config;
9093
}
9194
}
@@ -98,7 +101,7 @@ public boolean equals(Object o) {
98101
if (!(o instanceof RdfFormatterConfig)) {
99102
return false;
100103
}
101-
RdfFormatterConfig that = (RdfFormatterConfig) o;
104+
var that = (RdfFormatterConfig) o;
102105
return isFailOnWarning() == that.isFailOnWarning()
103106
&& Objects.equals(turtleFormatterVersion, that.turtleFormatterVersion);
104107
}

lib/src/main/java/com/diffplug/spotless/rdf/RdfFormatterStep.java

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2024-2025 DiffPlug
2+
* Copyright 2024-2026 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.
@@ -24,14 +24,18 @@
2424

2525
import com.diffplug.spotless.FormatterStep;
2626
import com.diffplug.spotless.JarState;
27+
import com.diffplug.spotless.Jvm;
2728
import com.diffplug.spotless.Provisioner;
2829

2930
public class RdfFormatterStep implements Serializable {
30-
public static final String LATEST_TURTLE_FORMATTER_VERSION = "1.2.13";
31+
public static final String LATEST_TURTLE_FORMATTER_VERSION = "2.0.1";
3132
@Serial
3233
private static final long serialVersionUID = 1L;
3334

34-
private static final String TURTLE_FORMATTER_COORDINATES = "de.atextor:turtle-formatter";
35+
private static final Jvm.Support<String> JVM_SUPPORT = Jvm.<String> support("cool-rdf-formatter")
36+
.add(25, "2.0.1");
37+
38+
private static final String TURTLE_FORMATTER_COORDINATES = "cool.rdf:cool-rdf-formatter";
3539

3640
private final JarState.Promised jarState;
3741
private final Map<String, String> turtleFormatterStyle;
@@ -51,14 +55,20 @@ public static State state(RdfFormatterStep step) {
5155

5256
public static RdfFormatterFunc formatterFunc(State state)
5357
throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
54-
return new RdfFormatterFunc(state);
58+
var formatterFunc = new RdfFormatterFunc(state);
59+
return (RdfFormatterFunc) JVM_SUPPORT.suggestLaterVersionOnError(state.config.getTurtleFormatterVersion(), formatterFunc);
5560
}
5661

5762
public RdfFormatterStep(JarState.Promised jarState, RdfFormatterConfig config,
5863
Map<String, String> turtleFormatterStyle) {
5964
this.jarState = jarState;
6065
this.turtleFormatterStyle = turtleFormatterStyle;
6166
this.config = config;
67+
JVM_SUPPORT.assertFormatterSupported(this.config.getTurtleFormatterVersion());
68+
}
69+
70+
static String getDefaultFormatterVersion() {
71+
return JVM_SUPPORT.getMinimumRequiredFormatterVersion();
6272
}
6373

6474
public static class State implements Serializable {
@@ -98,7 +108,7 @@ public boolean equals(Object o) {
98108
if (!(o instanceof State)) {
99109
return false;
100110
}
101-
State state = (State) o;
111+
var state = (State) o;
102112
return Objects.equals(getConfig(), state.getConfig()) && Objects.equals(
103113
getTurtleFormatterStyle(), state.getTurtleFormatterStyle())
104114
&& Objects.equals(

lib/src/main/java/com/diffplug/spotless/rdf/ReflectionHelper.java

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2024-2025 DiffPlug
2+
* Copyright 2024-2026 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.
@@ -18,7 +18,6 @@
1818
import java.io.File;
1919
import java.io.StringWriter;
2020
import java.lang.invoke.MethodHandles;
21-
import java.lang.reflect.Field;
2221
import java.lang.reflect.InvocationHandler;
2322
import java.lang.reflect.InvocationTargetException;
2423
import java.lang.reflect.Method;
@@ -64,7 +63,8 @@ class ReflectionHelper {
6463
private final Class<?> TurtleFormatFormattingStyleClass;
6564
private final Class<?> TurtleFormatFormattingStyleBuilderClass;
6665
private final Class<?> TurtleFormatFormatterClass;
67-
private final Class<?> TurtleFormatKnownPrefix;
66+
private final Class<?> CoolRdfPrefixesClass;
67+
private final Class<?> CoolRdfPrefixClass;
6868

6969
private final Method graphStream;
7070
private final Method graphFindTriple;
@@ -96,12 +96,11 @@ public ReflectionHelper(RdfFormatterStep.State state)
9696
this.JenaModelFactoryClass = classLoader.loadClass("org.apache.jena.rdf.model.ModelFactory");
9797
this.JenaLangClass = classLoader.loadClass("org.apache.jena.riot.Lang");
9898
this.JenaRDFFormatClass = classLoader.loadClass("org.apache.jena.riot.RDFFormat");
99-
this.TurtleFormatFormatterClass = classLoader.loadClass("de.atextor.turtle.formatter.TurtleFormatter");
100-
this.TurtleFormatFormattingStyleClass = classLoader.loadClass("de.atextor.turtle.formatter.FormattingStyle");
101-
Class<?>[] innerClasses = TurtleFormatFormattingStyleClass.getDeclaredClasses();
102-
this.TurtleFormatFormattingStyleBuilderClass = Arrays.stream(innerClasses)
103-
.filter(c -> "FormattingStyleBuilder".equals(c.getSimpleName())).findFirst().orElseThrow();
104-
this.TurtleFormatKnownPrefix = Arrays.stream(innerClasses).filter(c -> "KnownPrefix".equals(c.getSimpleName())).findFirst().orElseThrow();
99+
this.TurtleFormatFormatterClass = classLoader.loadClass("cool.rdf.formatter.TurtleFormatter");
100+
this.TurtleFormatFormattingStyleClass = classLoader.loadClass("cool.rdf.formatter.FormattingStyle");
101+
this.TurtleFormatFormattingStyleBuilderClass = TurtleFormatFormattingStyleClass.getMethod("builder").getReturnType();
102+
this.CoolRdfPrefixesClass = classLoader.loadClass("cool.rdf.core.Prefixes");
103+
this.CoolRdfPrefixClass = classLoader.loadClass("cool.rdf.core.model.RdfPrefix");
105104
this.getSubject = JenaStatementClass.getMethod("getSubject");
106105
this.getPredicate = JenaStatementClass.getMethod("getPredicate");
107106
this.getObject = JenaStatementClass.getMethod("getObject");
@@ -391,7 +390,7 @@ private Object instantiate(Type type, String stringRepresentation)
391390
.invoke(this.jenaModelInstance, namespace, localname);
392391
}
393392
}
394-
if (type.equals(TurtleFormatKnownPrefix)) {
393+
if (type.equals(CoolRdfPrefixClass)) {
395394
return getKnownPrefix(stringRepresentation);
396395
}
397396
throw new IllegalArgumentException("Cannot instantiate class %s from string representation %s".formatted(type, stringRepresentation));
@@ -404,7 +403,7 @@ private String tryToMakeUri(String stringRepresentation)
404403
//could be a known prefix
405404
String prefix = stringRepresentation.substring(0, colonIndex);
406405
Object knownPrefix = getKnownPrefix(prefix);
407-
String base = this.TurtleFormatKnownPrefix.getMethod("iri").invoke(knownPrefix).toString();
406+
String base = this.CoolRdfPrefixClass.getMethod("uri").invoke(knownPrefix).toString();
408407
return base + stringRepresentation.substring(colonIndex + 1);
409408
}
410409
// try to parse a URI - throws an IllegalArgumentException if it is not a URI
@@ -414,20 +413,16 @@ private String tryToMakeUri(String stringRepresentation)
414413

415414
private Object getKnownPrefix(String stringRepresentation)
416415
throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
417-
Field[] fields = TurtleFormatFormattingStyleClass.getDeclaredFields();
418416
List<String> options = new ArrayList<>();
419-
for (Field field : fields) {
420-
if (field.getType().equals(TurtleFormatKnownPrefix)) {
421-
Object knownPrefix = field.get(TurtleFormatFormattingStyleClass);
422-
String prefix = (String) TurtleFormatKnownPrefix.getMethod("prefix").invoke(knownPrefix);
423-
options.add(prefix);
424-
if (stringRepresentation.equals(prefix)) {
425-
return knownPrefix;
426-
}
417+
for (Object knownPrefix : CoolRdfPrefixesClass.getEnumConstants()) {
418+
String prefix = (String) CoolRdfPrefixClass.getMethod("prefix").invoke(knownPrefix);
419+
options.add(prefix);
420+
if (stringRepresentation.equals(prefix)) {
421+
return knownPrefix;
427422
}
428423
}
429-
throw new IllegalArgumentException("Unable to find FormattingStyle.KnownPrefix for prefix '%s'. Options are: %s".formatted(stringRepresentation, options.stream().collect(
430-
Collectors.joining(",\n\t", "\n\t", "\n"))));
424+
throw new IllegalArgumentException("Unable to find a known Cool RDF prefix for '%s'. Options are: %s".formatted(stringRepresentation, options.stream().collect(
425+
Collectors.joining(", "))));
431426
}
432427

433428
private static String[] split(String parameterValueAsString) {
@@ -439,7 +434,7 @@ private static String[] split(String parameterValueAsString) {
439434

440435
private Method getBuilderMethod(String optionName) {
441436
Method[] allMethods = TurtleFormatFormattingStyleBuilderClass.getDeclaredMethods();
442-
List<Method> methods = Arrays.stream(allMethods).filter(m -> m.getName().equals(optionName))
437+
List<Method> methods = Arrays.stream(allMethods).filter(m -> m.getName().equals(optionName) && m.getParameterCount() == 1)
443438
.collect(
444439
Collectors.toList());
445440
if (methods.isEmpty()) {

plugin-maven/CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
66
### Added
77
- Add support for Java formatting via [`prince-of-space`](https://github.com/agustafson/prince-of-space) with the new `<princeOfSpace>` step. ([#2991](https://github.com/diffplug/spotless/pull/2991))
88
### Changes
9+
- Replace RDF formatter library `de.atextor:turtle-formatter` (discontinued) with `cool.rdf:cool-rdf-formatter` (its new coordinates); the RDF/turtle formatter now requires Java 25+. ([#2995](https://github.com/diffplug/spotless/pull/2995))
910
- Bump default `greclipse` version to latest `4.39` -> `4.40`. ([#2989](https://github.com/diffplug/spotless/pull/2989))
1011
- Bump default `tabletest-formatter` version `1.1.1` -> `1.1.2`.
1112

plugin-maven/README.md

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1337,17 +1337,19 @@ List of generic configuration `parameters (type/default)`
13371337
the build fails for any of them. You can ignore warnings using this parameter. They will still be logged in the plugin's
13381338
output.
13391339
* `verify (boolean/true)`: If `true`, the content before and after formatting is parsed to an RDF model and compared for isomorphicity.
1340-
* `turtleFormatterVersion (string|RdfFormatterStep.LATEST_TURTLE_FORMATTER_VERSION)`: the version of turtle-formatter to use (see below).
1340+
* `turtleFormatterVersion (string|RdfFormatterStep.LATEST_TURTLE_FORMATTER_VERSION)`: the version of Cool RDF Formatter to use (see below).
13411341

13421342
### Supported RDF formats: only TTL (at the moment)
13431343

1344-
Formatting TTL is done using [turtle-formatter](https://github.com/atextor/turtle-formatter),
1345-
which is highly configurable (have a look at the [Style Documentation](https://github.com/atextor/turtle-formatter?tab=readme-ov-file#customizing-the-style))
1344+
Formatting TTL is done using [Cool RDF Formatter](https://github.com/cool-rdf/cool-rdf/tree/main/cool-rdf-formatter),
1345+
which is highly configurable (have a look at the [Style Documentation](https://github.com/cool-rdf/cool-rdf/tree/main/cool-rdf-formatter))
13461346
and will handle blank nodes the way you'd hope.
13471347

1348-
The style options can be configured via spotless. Wherever the style wants a URI (for example, for the `predicateOrder`, you can
1349-
use the abbreviated form if it is a `FormattingStyle.KnownPrefix` (currently `rdf`, `rdfs`, `xsd`, `owl`, `dcterms`)
1350-
Error messages will give you hints. To configure the TTL formatting style, pass the configuration parameters under `<turtle>`
1348+
The style options can be configured via spotless. Wherever the style wants a URI (for example, for the `predicateOrder`, you can
1349+
use the abbreviated form if it is a known `RdfPrefix` from Cool RDF's `Prefixes` enum.
1350+
Error messages will give you hints. To configure the TTL formatting style, pass the configuration parameters under `<turtle>`.
1351+
For example, Cool RDF Formatter versions which support `preserveBlankNodeLabelsAndOrdering` (default true) can set it with
1352+
`<preserveBlankNodeLabelsAndOrdering>false</preserveBlankNodeLabelsAndOrdering>`.Set it to `false`, to use Cool RDF's default behavior (stable blank node ordering).
13511353

13521354
### Examples
13531355
Minimal:
@@ -1371,10 +1373,11 @@ Configuring some generic and TTL options:
13711373
<format>
13721374
<failOnWarning>false</failOnWarning>
13731375
<verify>false</verify>
1374-
<turtleFormatterVersion>1.2.13</turtleFormatterVersion>
1376+
<turtleFormatterVersion>2.0.0</turtleFormatterVersion>
13751377
<turtle>
13761378
<alignPrefixes>RIGHT</alignPrefixes>
13771379
<enableDoubleFormatting>true</enableDoubleFormatting>
1380+
<preserveBlankNodeLabelsAndOrdering>false</preserveBlankNodeLabelsAndOrdering>
13781381
</turtle>
13791382
</format>
13801383
</rdf>
@@ -1383,7 +1386,7 @@ Configuring some generic and TTL options:
13831386
### Libraries and versions
13841387

13851388
RDF parsing is done via [Apache Jena](https://jena.apache.org/) in the version that
1386-
[turtle-formatter](https://github.com/atextor/turtle-formatter) depends on (not necessarily the latest).
1389+
[Cool RDF Formatter](https://github.com/cool-rdf/cool-rdf/tree/main/cool-rdf-formatter) depends on (not necessarily the latest).
13871390

13881391
## Protobuf
13891392

testlib/src/main/resources/rdf/ttl/expected/v1.2.12-default/shacl-shacl.ttl renamed to testlib/src/main/resources/rdf/ttl/expected/v2.0.0-default/shacl-shacl.ttl

File renamed without changes.

testlib/src/main/resources/rdf/ttl/expected/v1.2.12-default/shacl.ttl renamed to testlib/src/main/resources/rdf/ttl/expected/v2.0.0-default/shacl.ttl

File renamed without changes.

testlib/src/main/resources/rdf/ttl/expected/v1.2.12-style01/shacl-shacl.ttl renamed to testlib/src/main/resources/rdf/ttl/expected/v2.0.0-style01/shacl-shacl.ttl

File renamed without changes.

0 commit comments

Comments
 (0)