Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/scripts/update_renovate_changelogs.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
("libraries", "gson"): "gson",
("libraries", "json-simple"): "json-simple",
("libraries", "ktfmt"): "ktfmt",
("libraries", "open-java-format"): "open-java-format",
("libraries", "prince-of-space-core"): "prince-of-space",
("libraries", "scalafmt-core"): "scalafmt",
("libraries", "sortpom-sorter"): "sortpom",
Expand Down
3 changes: 3 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (

## [Unreleased]

### Added
- `OpenJavaFormatStep` for [open-java-format](https://github.com/openjavaformat/open-java-format), a fork of palantir-java-format. Requires JDK 21+.

### Changes
- Generate formatter defaults from version catalog. ([#3045](https://github.com/diffplug/spotless/pull/3045))
- Bump default `gson` version `2.13.2` -> `2.14.0`. ([#3045](https://github.com/diffplug/spotless/pull/3045))
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ lib('gherkin.GherkinUtilsStep') +'{{yes}} | {{yes}}
extra('groovy.GrEclipseFormatterStep') +'{{yes}} | {{yes}} | {{yes}} | {{no}} |',
lib('java.GoogleJavaFormatStep') +'{{yes}} | {{yes}} | {{yes}} | {{no}} |',
lib('java.ImportOrderStep') +'{{yes}} | {{yes}} | {{yes}} | {{no}} |',
lib('java.OpenJavaFormatStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |',
lib('java.PalantirJavaFormatStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |',
lib('java.PrinceOfSpaceStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |',
lib('java.RemoveUnusedImportsStep') +'{{yes}} | {{yes}} | {{yes}} | {{no}} |',
Expand Down Expand Up @@ -144,6 +145,7 @@ lib('yaml.JacksonYamlStep') +'{{yes}} | {{yes}}
| [`groovy.GrEclipseFormatterStep`](lib-extra/src/main/java/com/diffplug/spotless/extra/groovy/GrEclipseFormatterStep.java) | :+1: | :+1: | :+1: | :white_large_square: |
| [`java.GoogleJavaFormatStep`](lib/src/main/java/com/diffplug/spotless/java/GoogleJavaFormatStep.java) | :+1: | :+1: | :+1: | :white_large_square: |
| [`java.ImportOrderStep`](lib/src/main/java/com/diffplug/spotless/java/ImportOrderStep.java) | :+1: | :+1: | :+1: | :white_large_square: |
| [`java.OpenJavaFormatStep`](lib/src/main/java/com/diffplug/spotless/java/OpenJavaFormatStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: |
| [`java.PalantirJavaFormatStep`](lib/src/main/java/com/diffplug/spotless/java/PalantirJavaFormatStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: |
| [`java.PrinceOfSpaceStep`](lib/src/main/java/com/diffplug/spotless/java/PrinceOfSpaceStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: |
| [`java.RemoveUnusedImportsStep`](lib/src/main/java/com/diffplug/spotless/java/RemoveUnusedImportsStep.java) | :+1: | :+1: | :+1: | :white_large_square: |
Expand Down
1 change: 1 addition & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ google-java-format = "com.google.googlejavaformat:google-java-format:1.30.0"
gson = "com.google.code.gson:gson:2.14.0"
json-simple = "org.json:json:20210307"
ktfmt = "com.facebook:ktfmt:0.64"
open-java-format = "dev.openjavaformat:open-java-format:2.98.0.1"
palantir-java-format = "com.palantir.javaformat:palantir-java-format:1.1.0"
prince-of-space-core = "io.github.agustafson.princeofspace:prince-of-space-core:2.2.0"
scalafmt-core = "org.scalameta:scalafmt-core_2.13:3.11.5"
Expand Down
1 change: 1 addition & 0 deletions lib/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ buildConfig {
buildConfigField("VERSION_CLEANTHAT", libs.cleanthat.java.version)
buildConfigField("VERSION_GJF", libs.google.java.format.version)
buildConfigField("VERSION_JAVAPARSER", libs.javaparser.symbol.solver.core.version)
buildConfigField("VERSION_OPEN_JAVA_FORMAT", libs.open.java.format.version)
buildConfigField("VERSION_PALANTIR_JAVA_FORMAT", libs.palantir.java.format.default.version)
buildConfigField("VERSION_PRINCE_OF_SPACE", libs.prince.of.space.core.version)
buildConfigField("VERSION_TABLETEST_FORMATTER", libs.tabletest.formatter.core.version)
Expand Down
139 changes: 139 additions & 0 deletions lib/src/main/java/com/diffplug/spotless/java/OpenJavaFormatStep.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*
* Copyright 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.java;

import java.io.Serial;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.util.Objects;

import com.diffplug.spotless.FormatterFunc;
import com.diffplug.spotless.FormatterStep;
import com.diffplug.spotless.JarState;
import com.diffplug.spotless.Jvm;
import com.diffplug.spotless.Provisioner;

/** Wraps up <a href="https://github.com/openjavaformat/open-java-format">open-java-format</a>, a fork of
* <a href="https://github.com/palantir/palantir-java-format">palantir-java-format</a>, as a FormatterStep. */
public final class OpenJavaFormatStep implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private static final boolean DEFAULT_FORMAT_JAVADOC = false;
private static final String DEFAULT_STYLE = "OJF";
private static final String NAME = "open-java-format";
public static final String MAVEN_COORDINATE = "dev.openjavaformat:open-java-format:";
public static final String DEFAULT_VERSION = BuildConfig.VERSION_OPEN_JAVA_FORMAT; // requires Java 21+

/** The jar that contains the formatter. */
private final JarState.Promised jarState;
/** Version of the formatter jar. */
private final String formatterVersion;
private final String style;
/** Whether to format Java docs. */
private final boolean formatJavadoc;

private OpenJavaFormatStep(JarState.Promised jarState,
String formatterVersion,
String style,
boolean formatJavadoc) {
this.jarState = jarState;
this.formatterVersion = formatterVersion;
this.style = style;
this.formatJavadoc = formatJavadoc;
}

/** Creates a step which formats everything - code, import order, and unused imports. */
public static FormatterStep create(Provisioner provisioner) {
return create(defaultVersion(), provisioner);
}

/** Creates a step which formats everything - code, import order, and unused imports. */
public static FormatterStep create(String version, Provisioner provisioner) {
return create(version, defaultStyle(), provisioner);
}

/**
* Creates a step which formats code, import order, and unused imports, but not Java docs. And with the given format
* style.
*/
public static FormatterStep create(String version, String style, Provisioner provisioner) {
return create(version, style, DEFAULT_FORMAT_JAVADOC, provisioner);
}

/**
* Creates a step which formats everything - code, import order, unused imports, and Java docs. And with the given
* format style.
*/
public static FormatterStep create(String version, String style, boolean formatJavadoc, Provisioner provisioner) {
Objects.requireNonNull(version, "version");
Objects.requireNonNull(style, "style");
Objects.requireNonNull(provisioner, "provisioner");

return FormatterStep.create(NAME,
new OpenJavaFormatStep(JarState.promise(() -> JarState.from(MAVEN_COORDINATE + version, provisioner)), version, style, formatJavadoc),
OpenJavaFormatStep::equalityState,
State::createFormat);
}

/** Get default formatter version */
public static String defaultVersion() {
return DEFAULT_VERSION;
}

/** Get default style */
public static String defaultStyle() {
return DEFAULT_STYLE;
}

/** Get default for whether Java docs should be formatted */
public static boolean defaultFormatJavadoc() {
return DEFAULT_FORMAT_JAVADOC;
}

private State equalityState() {
return new State(jarState.get(), formatterVersion, style, formatJavadoc);
}

private static final class State implements Serializable {
@Serial
private static final long serialVersionUID = 1L;

private final JarState jarState;
private final String formatterVersion;
private final String style;
private final boolean formatJavadoc;

State(JarState jarState, String formatterVersion, String style, boolean formatJavadoc) {
if (Jvm.version() < 21) {
throw new IllegalStateException("open-java-format requires a JDK 21+ host runtime, this JVM is " + Jvm.version());
}
ModuleHelper.doOpenInternalPackagesIfRequired();
this.jarState = jarState;
this.formatterVersion = formatterVersion;
this.style = style;
this.formatJavadoc = formatJavadoc;
}

FormatterFunc createFormat() throws Exception {
final ClassLoader classLoader = jarState.getClassLoader();
// open-java-format keeps the palantir-java-format API, so the palantir glue drives it
final Class<?> formatterFunc = classLoader.loadClass("com.diffplug.spotless.glue.pjf.PalantirJavaFormatFormatterFunc");
// 1st arg is "style", 2nd arg is "formatJavadoc"
final Constructor<?> constructor = formatterFunc.getConstructor(String.class, boolean.class);
return (FormatterFunc) constructor.newInstance(style, formatJavadoc);
}
}
}
3 changes: 3 additions & 0 deletions plugin-gradle/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `3.27.0`).

## [Unreleased]
### Added
- `openJavaFormat()` for [open-java-format](https://github.com/openjavaformat/open-java-format), a fork of palantir-java-format. Requires JDK 21+.

### Changes
- Generate formatter defaults from version catalog. ([#3045](https://github.com/diffplug/spotless/pull/3045))
- Bump default `gson` version `2.13.2` -> `2.14.0`. ([#3045](https://github.com/diffplug/spotless/pull/3045))
Expand Down
15 changes: 14 additions & 1 deletion plugin-gradle/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ Spotless supports all of Gradle's built-in performance features (incremental bui
- [Git hook (optional)](#git-hook)
- [Linting](#linting)
- **Languages**
- [Java](#java) ([removeUnusedImports](#removeunusedimports), [forbidWildcardImports](#forbidwildcardimports), [expandWildcardImports](#expandwildcardimports), [forbidModuleImports](#forbidmoduleimports), [shortenFullyQualifiedTypes](#shortenfullyqualifiedtypes), [google-java-format](#google-java-format), [eclipse jdt](#eclipse-jdt), [clang-format](#clang-format), [prettier](#prettier), [palantir-java-format](#palantir-java-format), [prince-of-space](#prince-of-space), [formatAnnotations](#formatAnnotations), [cleanthat](#cleanthat), [tabletest-formatter](#tabletest-formatter), [IntelliJ IDEA](#intellij-idea))
- [Java](#java) ([removeUnusedImports](#removeunusedimports), [forbidWildcardImports](#forbidwildcardimports), [expandWildcardImports](#expandwildcardimports), [forbidModuleImports](#forbidmoduleimports), [shortenFullyQualifiedTypes](#shortenfullyqualifiedtypes), [google-java-format](#google-java-format), [eclipse jdt](#eclipse-jdt), [clang-format](#clang-format), [prettier](#prettier), [open-java-format](#open-java-format), [palantir-java-format](#palantir-java-format), [prince-of-space](#prince-of-space), [formatAnnotations](#formatAnnotations), [cleanthat](#cleanthat), [tabletest-formatter](#tabletest-formatter), [IntelliJ IDEA](#intellij-idea))
- [Groovy](#groovy) ([eclipse groovy](#eclipse-groovy))
- [Kotlin](#kotlin) ([ktfmt](#ktfmt), [ktlint](#ktlint), [diktat](#diktat), [tabletest-formatter](#tabletest-formatter-1), [prettier](#prettier))
- [Scala](#scala) ([scalafmt](#scalafmt))
Expand Down Expand Up @@ -361,6 +361,19 @@ spotless {
googleJavaFormat('1.8').aosp().reflowLongStrings().formatJavadoc(false).reorderImports(false).groupArtifact('com.google.googlejavaformat:google-java-format')
```

### open-java-format

[homepage](https://github.com/openjavaformat/open-java-format). [changelog](https://github.com/openjavaformat/open-java-format/releases). Requires a JDK 21+ host runtime.
```gradle
spotless {
java {
openJavaFormat()
// optional: you can specify a specific version and/or switch to AOSP/GOOGLE style
openJavaFormat('2.98.0.1').style("GOOGLE")
// optional: you can also format Javadocs
openJavaFormat('2.98.0.1').formatJavadoc(true)
```

### palantir-java-format

[homepage](https://github.com/palantir/palantir-java-format). [changelog](https://github.com/palantir/palantir-java-format/releases).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import com.diffplug.spotless.java.FormatAnnotationsStep;
import com.diffplug.spotless.java.GoogleJavaFormatStep;
import com.diffplug.spotless.java.ImportOrderStep;
import com.diffplug.spotless.java.OpenJavaFormatStep;
import com.diffplug.spotless.java.PalantirJavaFormatStep;
import com.diffplug.spotless.java.PrinceOfSpaceStep;
import com.diffplug.spotless.java.RemoveUnusedImportsStep;
Expand Down Expand Up @@ -273,6 +274,50 @@ private FormatterStep createStep() {
}
}

/** Uses the <a href="https://github.com/openjavaformat/open-java-format">open-java-format</a> jar to format source code. */
public OpenJavaFormatConfig openJavaFormat() {
return openJavaFormat(OpenJavaFormatStep.defaultVersion());
}

/**
* Uses the given version of <a href="https://github.com/openjavaformat/open-java-format">open-java-format</a> to format source code.
* <p>
* Limited to published versions. See <a href="https://github.com/diffplug/spotless/issues/33#issuecomment-252315095">issue #33</a>
* for a workaround for using snapshot versions.
*/
public OpenJavaFormatConfig openJavaFormat(String version) {
Objects.requireNonNull(version);
return new OpenJavaFormatConfig(version);
}

public class OpenJavaFormatConfig {
final String version;
String style;
boolean formatJavadoc;

OpenJavaFormatConfig(String version) {
this.version = Objects.requireNonNull(version);
this.style = OpenJavaFormatStep.defaultStyle();
addStep(createStep());
}

public OpenJavaFormatConfig style(String style) {
this.style = Objects.requireNonNull(style);
replaceStep(createStep());
return this;
}

public OpenJavaFormatConfig formatJavadoc(boolean formatJavadoc) {
this.formatJavadoc = formatJavadoc;
replaceStep(createStep());
return this;
}

private FormatterStep createStep() {
return OpenJavaFormatStep.create(version, style, formatJavadoc, provisioner());
}
}

/** Uses the <a href="https://github.com/palantir/palantir-java-format">palantir-java-format</a> jar to format source code. */
public PalantirJavaFormatConfig palantirJavaFormat() {
return palantirJavaFormat(PalantirJavaFormatStep.defaultVersion());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright 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.gradle.spotless;

import static org.junit.jupiter.api.condition.JRE.JAVA_21;

import java.io.IOException;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledForJreRange;

@EnabledForJreRange(min = JAVA_21) // open-java-format is compiled for Java 21
class OpenJavaFormatIntegrationTest extends GradleIntegrationHarness {
@Test
void integration() throws IOException {
setFile("build.gradle").toLines(
"plugins {",
" id 'com.diffplug.spotless'",
"}",
"repositories { mavenCentral() }",
"",
"spotless {",
" java {",
" target file('test.java')",
" openJavaFormat()",
" }",
"}");

setFile("test.java").toResource("java/palantirjavaformat/JavaCodeUnformatted.test");
gradleRunner().withArguments("spotlessApply").build();
assertFile("test.java").sameAsResource("java/palantirjavaformat/JavaCodeFormatted.test");

checkRunsThenUpToDate();
}

@Test
void formatJavaDoc() throws IOException {
setFile("build.gradle").toLines(
"plugins {",
" id 'com.diffplug.spotless'",
"}",
"repositories { mavenCentral() }",
"",
"spotless {",
" java {",
" target file('test.java')",
" openJavaFormat('2.98.0.1').formatJavadoc(true)",
" }",
"}");

setFile("test.java").toResource("java/palantirjavaformat/JavaCodeWithJavaDocUnformatted.test");
gradleRunner().withArguments("spotlessApply").build();
assertFile("test.java").sameAsResource("java/palantirjavaformat/JavaCodeWithJavaDocFormatted.test");

checkRunsThenUpToDate();
}
}
3 changes: 3 additions & 0 deletions plugin-maven/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `1.27.0`).

## [Unreleased]
### Added
- `<openJavaFormat>` for [open-java-format](https://github.com/openjavaformat/open-java-format), a fork of palantir-java-format. Requires JDK 21+.

### Changes
- Generate formatter defaults from version catalog. ([#3045](https://github.com/diffplug/spotless/pull/3045))
- Bump default `gson` version `2.13.2` -> `2.14.0`. ([#3045](https://github.com/diffplug/spotless/pull/3045))
Expand Down
Loading