diff --git a/.github/scripts/update_renovate_changelogs.py b/.github/scripts/update_renovate_changelogs.py index 61c64f425c..c397a757a5 100755 --- a/.github/scripts/update_renovate_changelogs.py +++ b/.github/scripts/update_renovate_changelogs.py @@ -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", diff --git a/CHANGES.md b/CHANGES.md index 75ae39ee0e..18ed590ecf 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -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)) diff --git a/README.md b/README.md index 921c50d2c5..d566491f61 100644 --- a/README.md +++ b/README.md @@ -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}} |', @@ -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: | diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2da62c3667..4fa8f2602e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -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" diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index f5b6e93ef2..26f1a206a7 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -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) diff --git a/lib/src/main/java/com/diffplug/spotless/java/OpenJavaFormatStep.java b/lib/src/main/java/com/diffplug/spotless/java/OpenJavaFormatStep.java new file mode 100644 index 0000000000..2878b19b15 --- /dev/null +++ b/lib/src/main/java/com/diffplug/spotless/java/OpenJavaFormatStep.java @@ -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 open-java-format, a fork of + * palantir-java-format, 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); + } + } +} diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md index 66f5fb2d0b..348dc79303 100644 --- a/plugin-gradle/CHANGES.md +++ b/plugin-gradle/CHANGES.md @@ -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)) diff --git a/plugin-gradle/README.md b/plugin-gradle/README.md index db4db7c376..4333064604 100644 --- a/plugin-gradle/README.md +++ b/plugin-gradle/README.md @@ -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)) @@ -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). diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java index 9411811322..6dee78c365 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java @@ -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; @@ -273,6 +274,50 @@ private FormatterStep createStep() { } } + /** Uses the open-java-format jar to format source code. */ + public OpenJavaFormatConfig openJavaFormat() { + return openJavaFormat(OpenJavaFormatStep.defaultVersion()); + } + + /** + * Uses the given version of open-java-format to format source code. + *

+ * Limited to published versions. See issue #33 + * 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 palantir-java-format jar to format source code. */ public PalantirJavaFormatConfig palantirJavaFormat() { return palantirJavaFormat(PalantirJavaFormatStep.defaultVersion()); diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/OpenJavaFormatIntegrationTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/OpenJavaFormatIntegrationTest.java new file mode 100644 index 0000000000..8626365d55 --- /dev/null +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/OpenJavaFormatIntegrationTest.java @@ -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(); + } +} diff --git a/plugin-maven/CHANGES.md b/plugin-maven/CHANGES.md index 674a6ee2ed..8158f8e59c 100644 --- a/plugin-maven/CHANGES.md +++ b/plugin-maven/CHANGES.md @@ -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 +- `` 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)) diff --git a/plugin-maven/README.md b/plugin-maven/README.md index 56aa4c18ce..83bde04bf7 100644 --- a/plugin-maven/README.md +++ b/plugin-maven/README.md @@ -40,7 +40,7 @@ user@machine repo % mvn spotless:check - [Git hook (optional)](#git-hook) - [Binding to maven phase](#binding-to-maven-phase) - **Languages** - - [Java](#java) ([removeUnusedImports](#removeunusedimports), [forbidWildcardImports](#forbidwildcardimports), [expandWildcardImports](#expandwildcardimports), [forbidModuleImports](#forbidmoduleimports), [shortenFullyQualifiedTypes](#shortenfullyqualifiedtypes), [google-java-format](#google-java-format), [eclipse jdt](#eclipse-jdt), [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), [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)) @@ -349,6 +349,18 @@ Types in `java.lang` and in the file's own package are shortened without adding ``` +### open-java-format + +[homepage](https://github.com/openjavaformat/open-java-format). [changelog](https://github.com/openjavaformat/open-java-format/releases). [code](https://github.com/diffplug/spotless/blob/main/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/OpenJavaFormat.java). Requires a JDK 21+ host runtime. + +```xml + + 2.98.0.1 + + false + +``` + ### palantir-java-format [homepage](https://github.com/palantir/palantir-java-format). [changelog](https://github.com/palantir/palantir-java-format/releases). [code](https://github.com/diffplug/spotless/blob/main/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/PalantirJavaFormat.java). diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/Java.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/Java.java index 996dbefea7..afefde2918 100644 --- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/Java.java +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/Java.java @@ -67,6 +67,10 @@ public void addImportOrder(ImportOrder importOrder) { addStepFactory(importOrder); } + public void addOpenJavaFormat(OpenJavaFormat openJavaFormat) { + addStepFactory(openJavaFormat); + } + public void addPalantirJavaFormat(PalantirJavaFormat palantirJavaFormat) { addStepFactory(palantirJavaFormat); } diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/OpenJavaFormat.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/OpenJavaFormat.java new file mode 100644 index 0000000000..885a52fd43 --- /dev/null +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/OpenJavaFormat.java @@ -0,0 +1,43 @@ +/* + * 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.maven.java; + +import org.apache.maven.plugins.annotations.Parameter; + +import com.diffplug.spotless.FormatterStep; +import com.diffplug.spotless.java.OpenJavaFormatStep; +import com.diffplug.spotless.maven.FormatterStepConfig; +import com.diffplug.spotless.maven.FormatterStepFactory; + +public class OpenJavaFormat implements FormatterStepFactory { + + @Parameter + private String version; + + @Parameter + private String style; + + @Parameter + private Boolean formatJavadoc; + + @Override + public FormatterStep newFormatterStep(FormatterStepConfig config) { + String version = this.version != null ? this.version : OpenJavaFormatStep.defaultVersion(); + String style = this.style != null ? this.style : OpenJavaFormatStep.defaultStyle(); + boolean formatJavadoc = this.formatJavadoc != null ? this.formatJavadoc : OpenJavaFormatStep.defaultFormatJavadoc(); + return OpenJavaFormatStep.create(version, style, formatJavadoc, config.getProvisioner()); + } +} diff --git a/plugin-maven/src/test/java/com/diffplug/spotless/maven/java/OpenJavaFormatTest.java b/plugin-maven/src/test/java/com/diffplug/spotless/maven/java/OpenJavaFormatTest.java new file mode 100644 index 0000000000..26502bd368 --- /dev/null +++ b/plugin-maven/src/test/java/com/diffplug/spotless/maven/java/OpenJavaFormatTest.java @@ -0,0 +1,54 @@ +/* + * 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.maven.java; + +import static org.junit.jupiter.api.condition.JRE.JAVA_21; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledForJreRange; + +import com.diffplug.spotless.maven.MavenIntegrationHarness; + +@EnabledForJreRange(min = JAVA_21) // open-java-format is compiled for Java 21 +class OpenJavaFormatTest extends MavenIntegrationHarness { + @Test + void specificVersionDefaultStyle() throws Exception { + writePomWithJavaSteps( + "", + " 2.98.0.1", + ""); + + runTest("java/palantirjavaformat/JavaCodeFormatted.test", "java/palantirjavaformat/JavaCodeUnformatted.test"); + } + + @Test + void formatJavaDoc() throws Exception { + writePomWithJavaSteps( + "", + " 2.98.0.1", + " true", + ""); + + runTest("java/palantirjavaformat/JavaCodeWithJavaDocFormatted.test", "java/palantirjavaformat/JavaCodeWithJavaDocUnformatted.test"); + } + + private void runTest(String targetResource, String sourceResource) throws Exception { + String path = "src/main/java/test.java"; + setFile(path).toResource(sourceResource); + mavenRunner().withArguments("spotless:apply").runNoError(); + assertFile(path).sameAsResource(targetResource); + } +} diff --git a/testlib/src/test/java/com/diffplug/spotless/java/OpenJavaFormatStepTest.java b/testlib/src/test/java/com/diffplug/spotless/java/OpenJavaFormatStepTest.java new file mode 100644 index 0000000000..52ea1a8abf --- /dev/null +++ b/testlib/src/test/java/com/diffplug/spotless/java/OpenJavaFormatStepTest.java @@ -0,0 +1,94 @@ +/* + * 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 static org.junit.jupiter.api.condition.JRE.JAVA_21; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledForJreRange; + +import com.diffplug.spotless.FormatterStep; +import com.diffplug.spotless.ResourceHarness; +import com.diffplug.spotless.SerializableEqualityTester; +import com.diffplug.spotless.StepHarness; +import com.diffplug.spotless.TestProvisioner; + +// open-java-format is compiled for Java 21. It formats exactly like palantir-java-format, so it shares its resources. +@EnabledForJreRange(min = JAVA_21) +class OpenJavaFormatStepTest extends ResourceHarness { + + @Test + void jvm13Features() throws Exception { + try (StepHarness step = StepHarness.forStep(OpenJavaFormatStep.create(TestProvisioner.mavenCentral()))) { + step.testResource("java/palantirjavaformat/TextBlock.dirty", "java/palantirjavaformat/TextBlock.clean"); + } + } + + @Test + void behavior() throws Exception { + FormatterStep step = OpenJavaFormatStep.create(TestProvisioner.mavenCentral()); + StepHarness.forStep(step) + .testResource("java/palantirjavaformat/JavaCodeUnformatted.test", "java/palantirjavaformat/JavaCodeFormatted.test") + .testResource("java/palantirjavaformat/JavaCodeWithLicenseUnformatted.test", "java/palantirjavaformat/JavaCodeWithLicenseFormatted.test") + .testResource("java/palantirjavaformat/JavaCodeWithPackageUnformatted.test", "java/palantirjavaformat/JavaCodeWithPackageFormatted.test"); + } + + @Test + void formatJavadoc() throws Exception { + FormatterStep step = OpenJavaFormatStep.create(OpenJavaFormatStep.defaultVersion(), "OJF", true, TestProvisioner.mavenCentral()); + StepHarness.forStep(step) + .testResource("java/palantirjavaformat/JavaCodeWithJavaDocUnformatted.test", "java/palantirjavaformat/JavaCodeWithJavaDocFormatted.test") + .testResource("java/palantirjavaformat/JavaCodeWithPackageUnformatted.test", "java/palantirjavaformat/JavaCodeWithPackageFormatted.test"); + } + + @Test + void behaviorWithGoogleStyle() throws Exception { + FormatterStep step = OpenJavaFormatStep.create(OpenJavaFormatStep.defaultVersion(), "GOOGLE", TestProvisioner.mavenCentral()); + StepHarness.forStep(step) + .testResource("java/palantirjavaformat/JavaCodeUnformatted.test", "java/palantirjavaformat/JavaCodeFormattedGoogle.test") + .testResource("java/palantirjavaformat/JavaCodeWithLicenseUnformatted.test", "java/palantirjavaformat/JavaCodeWithLicenseFormattedGoogle.test") + .testResource("java/palantirjavaformat/JavaCodeWithPackageUnformatted.test", "java/palantirjavaformat/JavaCodeWithPackageFormattedGoogle.test"); + } + + @Test + void equality() { + new SerializableEqualityTester() { + String style = "OJF"; + boolean formatJavadoc; + + @Override + protected void setupTest(API api) { + // same settings == same + api.areDifferentThan(); + + // change the style, and it's different + style = "AOSP"; + api.areDifferentThan(); + style = "OJF"; + + // change the format Java doc flag, and it's different + formatJavadoc = true; + api.areDifferentThan(); + formatJavadoc = false; + } + + @Override + protected FormatterStep create() { + return OpenJavaFormatStep.create(OpenJavaFormatStep.defaultVersion(), style, formatJavadoc, TestProvisioner.mavenCentral()); + } + }.testEquals(); + } +}