From 969c6547fe460e906fe75240bc082799d1979f5a Mon Sep 17 00:00:00 2001 From: toyeshhm Date: Sun, 8 Nov 2026 10:01:36 -0600 Subject: [PATCH 1/3] Fix DBeaver IndexOutOfBoundsException when a file starts with SET --- CHANGES.md | 1 + .../sql/dbeaver/SQLTokenizedFormatter.java | 4 ++++ plugin-gradle/CHANGES.md | 1 + plugin-maven/CHANGES.md | 1 + .../main/resources/sql/dbeaver/set-mode.clean | 2 ++ .../main/resources/sql/dbeaver/set-mode.dirty | 1 + .../sql/DBeaverSQLFormatterStepTest.java | 22 ++++++++++++++++++- 7 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 testlib/src/main/resources/sql/dbeaver/set-mode.clean create mode 100644 testlib/src/main/resources/sql/dbeaver/set-mode.dirty diff --git a/CHANGES.md b/CHANGES.md index eb93716703..5a63e97410 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,6 +12,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ## [Unreleased] ### Fixed - GrEclipse no longer leaves a `file%3A...` directory in the working directory. ([#2019](https://github.com/diffplug/spotless/issues/2019)) +- DBeaver SQL formatter no longer prints an `IndexOutOfBoundsException` when a file starts with a keyword like `SET`. ([#1552](https://github.com/diffplug/spotless/issues/1552)) ## [4.11.0] - 2026-11-02 ### Added diff --git a/lib/src/main/java/com/diffplug/spotless/sql/dbeaver/SQLTokenizedFormatter.java b/lib/src/main/java/com/diffplug/spotless/sql/dbeaver/SQLTokenizedFormatter.java index bad5d4a64a..af38b1631b 100644 --- a/lib/src/main/java/com/diffplug/spotless/sql/dbeaver/SQLTokenizedFormatter.java +++ b/lib/src/main/java/com/diffplug/spotless/sql/dbeaver/SQLTokenizedFormatter.java @@ -431,6 +431,10 @@ && isCommentLine(SQL_DIALECT, token.getString()) boolean isDelimiter = statementDelimiters.contains(token.getString().toUpperCase(Locale.ENGLISH)); if (!isDelimiter) { + if (argIndex == 0) { + // nothing precedes the first token, so there is no line to break + return 0; + } token = argList.get(argIndex - 1); if (token.getType() == TokenType.SPACE) { token.setString(s.toString()); diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md index 5aaef36403..f4217fd640 100644 --- a/plugin-gradle/CHANGES.md +++ b/plugin-gradle/CHANGES.md @@ -5,6 +5,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ## [Unreleased] ### Fixed - GrEclipse no longer leaves a `file%3A...` directory in the working directory. ([#2019](https://github.com/diffplug/spotless/issues/2019)) +- DBeaver SQL formatter no longer prints an `IndexOutOfBoundsException` when a file starts with a keyword like `SET`. ([#1552](https://github.com/diffplug/spotless/issues/1552)) ## [8.11.0] - 2026-11-02 ### Added diff --git a/plugin-maven/CHANGES.md b/plugin-maven/CHANGES.md index 4081c6671e..0c8019dc49 100644 --- a/plugin-maven/CHANGES.md +++ b/plugin-maven/CHANGES.md @@ -5,6 +5,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ## [Unreleased] ### Fixed - GrEclipse no longer leaves a `file%3A...` directory in the working directory. ([#2019](https://github.com/diffplug/spotless/issues/2019)) +- DBeaver SQL formatter no longer prints an `IndexOutOfBoundsException` when a file starts with a keyword like `SET`. ([#1552](https://github.com/diffplug/spotless/issues/1552)) ## [3.11.0] - 2026-11-02 ### Added diff --git a/testlib/src/main/resources/sql/dbeaver/set-mode.clean b/testlib/src/main/resources/sql/dbeaver/set-mode.clean new file mode 100644 index 0000000000..30e7fdc679 --- /dev/null +++ b/testlib/src/main/resources/sql/dbeaver/set-mode.clean @@ -0,0 +1,2 @@ +SET +MODE PostgreSQL; diff --git a/testlib/src/main/resources/sql/dbeaver/set-mode.dirty b/testlib/src/main/resources/sql/dbeaver/set-mode.dirty new file mode 100644 index 0000000000..81ae754394 --- /dev/null +++ b/testlib/src/main/resources/sql/dbeaver/set-mode.dirty @@ -0,0 +1 @@ +SET MODE PostgreSQL; diff --git a/testlib/src/test/java/com/diffplug/spotless/sql/DBeaverSQLFormatterStepTest.java b/testlib/src/test/java/com/diffplug/spotless/sql/DBeaverSQLFormatterStepTest.java index 648ddc6669..24c6be0b19 100644 --- a/testlib/src/test/java/com/diffplug/spotless/sql/DBeaverSQLFormatterStepTest.java +++ b/testlib/src/test/java/com/diffplug/spotless/sql/DBeaverSQLFormatterStepTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 DiffPlug + * 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. @@ -15,7 +15,11 @@ */ package com.diffplug.spotless.sql; +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.PrintStream; import java.util.Collections; import java.util.List; @@ -39,6 +43,22 @@ void behavior() { .testResource("sql/dbeaver/jdbi-params.dirty", "sql/dbeaver/jdbi-params.clean"); } + @Test + void keywordAtStartOfFileDoesNotThrow() { + // https://github.com/diffplug/spotless/issues/1552 + PrintStream originalErr = System.err; + ByteArrayOutputStream err = new ByteArrayOutputStream(); + System.setErr(new PrintStream(err, true)); + try { + FormatterStep step = DBeaverSQLFormatterStep.create(Collections.emptySet()); + StepHarness.forStep(step) + .testResource("sql/dbeaver/set-mode.dirty", "sql/dbeaver/set-mode.clean"); + } finally { + System.setErr(originalErr); + } + assertThat(err.toString()).isEmpty(); + } + @Test void behaviorWithConfigFile() { FormatterStep step = DBeaverSQLFormatterStep.create(createTestFiles("sql/dbeaver/sqlConfig.properties")); From 52d2191196b6da530e74d87b88be3de6c701f191 Mon Sep 17 00:00:00 2001 From: Guillaume Laforge Date: Mon, 9 Nov 2026 10:32:02 +0100 Subject: [PATCH 2/3] Add groupArtifact option to palantirJavaFormat --- .../spotless/java/PalantirJavaFormatStep.java | 22 ++++++++++++-- plugin-gradle/README.md | 2 ++ .../gradle/spotless/JavaExtension.java | 10 ++++++- .../PalantirJavaFormatIntegrationTest.java | 26 ++++++++++++++++ plugin-maven/README.md | 2 ++ .../maven/java/PalantirJavaFormat.java | 7 +++-- .../maven/java/PalantirJavaFormatTest.java | 16 ++++++++++ .../java/PalantirJavaFormatStepTest.java | 30 ++++++++++++++++++- 8 files changed, 109 insertions(+), 6 deletions(-) diff --git a/lib/src/main/java/com/diffplug/spotless/java/PalantirJavaFormatStep.java b/lib/src/main/java/com/diffplug/spotless/java/PalantirJavaFormatStep.java index 5840cd4073..cb658bbcb2 100644 --- a/lib/src/main/java/com/diffplug/spotless/java/PalantirJavaFormatStep.java +++ b/lib/src/main/java/com/diffplug/spotless/java/PalantirJavaFormatStep.java @@ -33,7 +33,8 @@ public final class PalantirJavaFormatStep implements Serializable { private static final boolean DEFAULT_FORMAT_JAVADOC = false; private static final String DEFAULT_STYLE = "PALANTIR"; private static final String NAME = "palantir-java-format"; - public static final String MAVEN_COORDINATE = "com.palantir.javaformat:palantir-java-format:"; + private static final String DEFAULT_GROUP_ARTIFACT = "com.palantir.javaformat:palantir-java-format"; + public static final String MAVEN_COORDINATE = DEFAULT_GROUP_ARTIFACT + ":"; public static final String DEFAULT_VERSION = BuildConfig.VERSION_PALANTIR_JAVA_FORMAT; // compatible with Java 11+ /** The jar that contains the formatter. */ @@ -77,16 +78,33 @@ public static FormatterStep create(String version, String style, Provisioner pro * format style. */ public static FormatterStep create(String version, String style, boolean formatJavadoc, Provisioner provisioner) { + return create(defaultGroupArtifact(), version, style, formatJavadoc, provisioner); + } + + /** + * Creates a step which formats everything - groupArtifact, code, import order, unused imports, and Java docs. And with the given + * format style. + */ + public static FormatterStep create(String groupArtifact, String version, String style, boolean formatJavadoc, Provisioner provisioner) { + Objects.requireNonNull(groupArtifact, "groupArtifact"); + if (groupArtifact.chars().filter(ch -> ch == ':').count() != 1) { + throw new IllegalArgumentException("groupArtifact must be in the form 'groupId:artifactId'"); + } Objects.requireNonNull(version, "version"); Objects.requireNonNull(style, "style"); Objects.requireNonNull(provisioner, "provisioner"); return FormatterStep.create(NAME, - new PalantirJavaFormatStep(JarState.promise(() -> JarState.from(MAVEN_COORDINATE + version, provisioner)), version, style, formatJavadoc), + new PalantirJavaFormatStep(JarState.promise(() -> JarState.from(groupArtifact + ":" + version, provisioner)), version, style, formatJavadoc), PalantirJavaFormatStep::equalityState, State::createFormat); } + /** Get default formatter group and artifact */ + public static String defaultGroupArtifact() { + return DEFAULT_GROUP_ARTIFACT; + } + /** Get default formatter version */ public static String defaultVersion() { return DEFAULT_VERSION; diff --git a/plugin-gradle/README.md b/plugin-gradle/README.md index 019abb7008..786b07e429 100644 --- a/plugin-gradle/README.md +++ b/plugin-gradle/README.md @@ -397,6 +397,8 @@ spotless { palantirJavaFormat('2.9.0').style("GOOGLE") // optional: you can also format Javadocs, requires at least Palantir 2.39.0 palantirJavaFormat('2.39.0').formatJavadoc(true) + // optional: you can also use custom group artifact (you probably don't need this) + palantirJavaFormat('2.39.0').groupArtifact('com.palantir.javaformat:palantir-java-format') ``` ### prince-of-space 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 f065c14f1b..ed7fa4151f 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 @@ -312,15 +312,23 @@ public PalantirJavaFormatConfig palantirJavaFormat(String version) { public class PalantirJavaFormatConfig { final String version; + String groupArtifact; String style; boolean formatJavadoc; PalantirJavaFormatConfig(String version) { this.version = Objects.requireNonNull(version); + this.groupArtifact = PalantirJavaFormatStep.defaultGroupArtifact(); this.style = PalantirJavaFormatStep.defaultStyle(); addStep(createStep()); } + public PalantirJavaFormatConfig groupArtifact(String groupArtifact) { + this.groupArtifact = Objects.requireNonNull(groupArtifact); + replaceStep(createStep()); + return this; + } + public PalantirJavaFormatConfig style(String style) { this.style = Objects.requireNonNull(style); replaceStep(createStep()); @@ -334,7 +342,7 @@ public PalantirJavaFormatConfig formatJavadoc(boolean formatJavadoc) { } private FormatterStep createStep() { - return PalantirJavaFormatStep.create(version, style, formatJavadoc, provisioner()); + return PalantirJavaFormatStep.create(groupArtifact, version, style, formatJavadoc, provisioner()); } } diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/PalantirJavaFormatIntegrationTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/PalantirJavaFormatIntegrationTest.java index 02538ff763..ba582e9fab 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/PalantirJavaFormatIntegrationTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/PalantirJavaFormatIntegrationTest.java @@ -15,9 +15,12 @@ */ 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; class PalantirJavaFormatIntegrationTest extends GradleIntegrationHarness { @Test @@ -67,4 +70,27 @@ void formatJavaDoc() throws IOException { checkRunsThenUpToDate(); } + + @Test + @EnabledForJreRange(min = JAVA_21) + void customGroupArtifact() throws IOException { + setFile("build.gradle").toLines( + "plugins {", + " id 'com.diffplug.spotless'", + "}", + "repositories { mavenCentral() }", + "", + "spotless {", + " java {", + " target file('test.java')", + " palantirJavaFormat('21.0.2').groupArtifact('dev.openjavaformat:open-java-format')", + " }", + "}"); + + setFile("test.java").toResource("java/palantirjavaformat/JavaCodeUnformatted.test"); + gradleRunner().withArguments("spotlessApply").build(); + assertFile("test.java").sameAsResource("java/palantirjavaformat/JavaCodeFormatted.test"); + + checkRunsThenUpToDate(); + } } diff --git a/plugin-maven/README.md b/plugin-maven/README.md index 3fae9b808d..dc3dcc1968 100644 --- a/plugin-maven/README.md +++ b/plugin-maven/README.md @@ -363,6 +363,8 @@ Note: when enabling `AOSP` style, `reorderImports` needs to be enabled to apply 2.39.0 false + + com.palantir.javaformat:palantir-java-format ``` diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/PalantirJavaFormat.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/PalantirJavaFormat.java index 674238d2e8..49bf13b893 100644 --- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/PalantirJavaFormat.java +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/PalantirJavaFormat.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 DiffPlug + * 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. @@ -23,6 +23,8 @@ import com.diffplug.spotless.maven.FormatterStepFactory; public class PalantirJavaFormat implements FormatterStepFactory { + @Parameter + private String groupArtifact; @Parameter private String version; @@ -35,9 +37,10 @@ public class PalantirJavaFormat implements FormatterStepFactory { @Override public FormatterStep newFormatterStep(FormatterStepConfig config) { + String groupArtifact = this.groupArtifact != null ? this.groupArtifact : PalantirJavaFormatStep.defaultGroupArtifact(); String version = this.version != null ? this.version : PalantirJavaFormatStep.defaultVersion(); String style = this.style != null ? this.style : PalantirJavaFormatStep.defaultStyle(); boolean formatJavadoc = this.formatJavadoc != null ? this.formatJavadoc : PalantirJavaFormatStep.defaultFormatJavadoc(); - return PalantirJavaFormatStep.create(version, style, formatJavadoc, config.getProvisioner()); + return PalantirJavaFormatStep.create(groupArtifact, version, style, formatJavadoc, config.getProvisioner()); } } diff --git a/plugin-maven/src/test/java/com/diffplug/spotless/maven/java/PalantirJavaFormatTest.java b/plugin-maven/src/test/java/com/diffplug/spotless/maven/java/PalantirJavaFormatTest.java index ffac52c6af..821766f9e9 100644 --- a/plugin-maven/src/test/java/com/diffplug/spotless/maven/java/PalantirJavaFormatTest.java +++ b/plugin-maven/src/test/java/com/diffplug/spotless/maven/java/PalantirJavaFormatTest.java @@ -15,8 +15,11 @@ */ package com.diffplug.spotless.maven.java; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + import org.junit.jupiter.api.Test; +import com.diffplug.spotless.Jvm; import com.diffplug.spotless.java.PalantirJavaFormatStep; import com.diffplug.spotless.maven.MavenIntegrationHarness; @@ -52,6 +55,19 @@ void formatJavaDoc() throws Exception { runTest("java/palantirjavaformat/JavaCodeWithJavaDocFormatted.test", "java/palantirjavaformat/JavaCodeWithJavaDocUnformatted.test"); } + @Test + void customGroupArtifact() throws Exception { + assumeTrue(Jvm.version() >= 21, "open-java-format 21.0.2 requires JVM 21+"); + + writePomWithJavaSteps( + "", + " dev.openjavaformat:open-java-format", + " 21.0.2", + ""); + + runTest("java/palantirjavaformat/JavaCodeFormatted.test", "java/palantirjavaformat/JavaCodeUnformatted.test"); + } + private void runTest(String targetResource, String sourceResource) throws Exception { String path = "src/main/java/test.java"; setFile(path).toResource(sourceResource); diff --git a/testlib/src/test/java/com/diffplug/spotless/java/PalantirJavaFormatStepTest.java b/testlib/src/test/java/com/diffplug/spotless/java/PalantirJavaFormatStepTest.java index 2f37e53326..03a82cd68e 100644 --- a/testlib/src/test/java/com/diffplug/spotless/java/PalantirJavaFormatStepTest.java +++ b/testlib/src/test/java/com/diffplug/spotless/java/PalantirJavaFormatStepTest.java @@ -15,7 +15,11 @@ */ package com.diffplug.spotless.java; +import static org.junit.jupiter.api.Assertions.assertThrows; +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; @@ -67,9 +71,28 @@ void behaviorWithGoogleStyle() throws Exception { .testResource("java/palantirjavaformat/JavaCodeWithPackageUnformatted.test", "java/palantirjavaformat/JavaCodeWithPackageFormattedGoogle.test"); } + @Test + @EnabledForJreRange(min = JAVA_21) + void behaviorWithCustomGroupArtifact() throws Exception { + FormatterStep step = PalantirJavaFormatStep.create("dev.openjavaformat:open-java-format", "21.0.2", PalantirJavaFormatStep.defaultStyle(), PalantirJavaFormatStep.defaultFormatJavadoc(), 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 invalidGroupArtifact() { + assertThrows(IllegalArgumentException.class, + () -> PalantirJavaFormatStep.create("com.palantir.javaformat", "1.1.0", "PALANTIR", false, TestProvisioner.mavenCentral())); + assertThrows(IllegalArgumentException.class, + () -> PalantirJavaFormatStep.create("com.palantir.javaformat:palantir-java-format:1.1.0", "1.1.0", "PALANTIR", false, TestProvisioner.mavenCentral())); + } + @Test void equality() { new SerializableEqualityTester() { + String groupArtifact = PalantirJavaFormatStep.defaultGroupArtifact(); String version = "1.1.0"; String style = ""; boolean formatJavadoc; @@ -93,12 +116,17 @@ protected void setupTest(API api) { formatJavadoc = true; api.areDifferentThan(); formatJavadoc = false; + + // change the groupArtifact, and it's different + groupArtifact = "com.palantir.javaformat:palantir-java-format-spi"; + api.areDifferentThan(); + groupArtifact = PalantirJavaFormatStep.defaultGroupArtifact(); } @Override protected FormatterStep create() { String finalVersion = this.version; - return PalantirJavaFormatStep.create(finalVersion, style, formatJavadoc, TestProvisioner.mavenCentral()); + return PalantirJavaFormatStep.create(groupArtifact, finalVersion, style, formatJavadoc, TestProvisioner.mavenCentral()); } }.testEquals(); } From 05eaa1be09594a9a34c84de87938f81793684ed6 Mon Sep 17 00:00:00 2001 From: Guillaume Laforge Date: Mon, 9 Nov 2026 10:33:44 +0100 Subject: [PATCH 3/3] Update changelogs for #3179 --- CHANGES.md | 2 ++ plugin-gradle/CHANGES.md | 2 ++ plugin-maven/CHANGES.md | 2 ++ 3 files changed, 6 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index eb93716703..9f2e69de63 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,6 +10,8 @@ This document is intended for Spotless developers. We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `1.27.0`). ## [Unreleased] +### Added +- Add a `groupArtifact` option to `PalantirJavaFormatStep`. ([#3179](https://github.com/diffplug/spotless/pull/3179)) ### Fixed - GrEclipse no longer leaves a `file%3A...` directory in the working directory. ([#2019](https://github.com/diffplug/spotless/issues/2019)) diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md index 5aaef36403..0b3d67032b 100644 --- a/plugin-gradle/CHANGES.md +++ b/plugin-gradle/CHANGES.md @@ -3,6 +3,8 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `3.27.0`). ## [Unreleased] +### Added +- Add `groupArtifact(String)` to `palantirJavaFormat()`. ([#3179](https://github.com/diffplug/spotless/pull/3179)) ### Fixed - GrEclipse no longer leaves a `file%3A...` directory in the working directory. ([#2019](https://github.com/diffplug/spotless/issues/2019)) diff --git a/plugin-maven/CHANGES.md b/plugin-maven/CHANGES.md index 4081c6671e..f906db9563 100644 --- a/plugin-maven/CHANGES.md +++ b/plugin-maven/CHANGES.md @@ -3,6 +3,8 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `1.27.0`). ## [Unreleased] +### Added +- Add a `` parameter to ``. ([#3179](https://github.com/diffplug/spotless/pull/3179)) ### Fixed - GrEclipse no longer leaves a `file%3A...` directory in the working directory. ([#2019](https://github.com/diffplug/spotless/issues/2019))