From a0a18b6d8477eb78402be6ae41d4204c7f2ec2be Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Sun, 7 Apr 2024 16:07:44 -0700 Subject: [PATCH 1/9] Unify `FormatterStep.Strict` (which adds LazyForwardingEquality) into `FormatterStepImpl` (its only direct subclass). --- .../com/diffplug/spotless/FormatterStep.java | 22 ------------------- .../diffplug/spotless/FormatterStepImpl.java | 17 ++++++++++---- 2 files changed, 13 insertions(+), 26 deletions(-) diff --git a/lib/src/main/java/com/diffplug/spotless/FormatterStep.java b/lib/src/main/java/com/diffplug/spotless/FormatterStep.java index 070e502a0c..9ab0dd5a9a 100644 --- a/lib/src/main/java/com/diffplug/spotless/FormatterStep.java +++ b/lib/src/main/java/com/diffplug/spotless/FormatterStep.java @@ -70,28 +70,6 @@ default FormatterStep filterByFile(SerializableFileFilter filter) { return new FilterByFileFormatterStep(this, filter); } - /** - * Implements a FormatterStep in a strict way which guarantees correct and lazy implementation - * of up-to-date checks. This maximizes performance for cases where the FormatterStep is not - * actually needed (e.g. don't load eclipse setting file unless this step is actually running) - * while also ensuring that Gradle can detect changes in a step's settings to determine that - * it needs to rerun a format. - */ - abstract class Strict extends LazyForwardingEquality implements FormatterStep { - private static final long serialVersionUID = 1L; - - /** - * Implements the formatting function strictly in terms - * of the input data and the result of {@link #calculateState()}. - */ - protected abstract String format(State state, String rawUnix, File file) throws Exception; - - @Override - public final String format(String rawUnix, File file) throws Exception { - return format(state(), rawUnix, file); - } - } - /** * @param name * The name of the formatter step. diff --git a/lib/src/main/java/com/diffplug/spotless/FormatterStepImpl.java b/lib/src/main/java/com/diffplug/spotless/FormatterStepImpl.java index 8e2982d6db..5efdadbd6e 100644 --- a/lib/src/main/java/com/diffplug/spotless/FormatterStepImpl.java +++ b/lib/src/main/java/com/diffplug/spotless/FormatterStepImpl.java @@ -20,8 +20,6 @@ import java.util.Objects; import java.util.Random; -import com.diffplug.spotless.FormatterStep.Strict; - import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; /** @@ -32,8 +30,8 @@ * from the API. */ @SuppressFBWarnings("SE_TRANSIENT_FIELD_NOT_RESTORED") -abstract class FormatterStepImpl extends Strict { - private static final long serialVersionUID = 1L; +abstract class FormatterStepImpl extends LazyForwardingEquality implements FormatterStep { + private static final long serialVersionUID = 2L; /** Transient because only the state matters. */ final transient String name; @@ -51,6 +49,17 @@ public String getName() { return name; } + /** + * Implements the formatting function strictly in terms + * of the input data and the result of {@link #calculateState()}. + */ + protected abstract String format(State state, String rawUnix, File file) throws Exception; + + @Override + public final String format(String rawUnix, File file) throws Exception { + return format(state(), rawUnix, file); + } + @Override protected State calculateState() throws Exception { // LazyForwardingEquality guarantees that this will only be called once, and keeping toFormat From 7a3c786940cb26c7cde76d180e817cffd33eb4d7 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Sun, 7 Apr 2024 16:12:36 -0700 Subject: [PATCH 2/9] Move `NeverUpToDateStep` out of `FormatterStepImpl` into its own class. --- .../com/diffplug/spotless/FormatterStep.java | 2 +- .../diffplug/spotless/FormatterStepImpl.java | 35 ------------ .../diffplug/spotless/NeverUpToDateStep.java | 56 +++++++++++++++++++ 3 files changed, 57 insertions(+), 36 deletions(-) create mode 100644 lib/src/main/java/com/diffplug/spotless/NeverUpToDateStep.java diff --git a/lib/src/main/java/com/diffplug/spotless/FormatterStep.java b/lib/src/main/java/com/diffplug/spotless/FormatterStep.java index 9ab0dd5a9a..567712eaab 100644 --- a/lib/src/main/java/com/diffplug/spotless/FormatterStep.java +++ b/lib/src/main/java/com/diffplug/spotless/FormatterStep.java @@ -163,7 +163,7 @@ static FormatterStep create( static FormatterStep createNeverUpToDateLazy( String name, ThrowingEx.Supplier functionSupplier) { - return new FormatterStepImpl.NeverUpToDate(name, functionSupplier); + return new NeverUpToDateStep(name, functionSupplier); } /** diff --git a/lib/src/main/java/com/diffplug/spotless/FormatterStepImpl.java b/lib/src/main/java/com/diffplug/spotless/FormatterStepImpl.java index 5efdadbd6e..14b04a178d 100644 --- a/lib/src/main/java/com/diffplug/spotless/FormatterStepImpl.java +++ b/lib/src/main/java/com/diffplug/spotless/FormatterStepImpl.java @@ -18,7 +18,6 @@ import java.io.File; import java.io.Serializable; import java.util.Objects; -import java.util.Random; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; @@ -100,40 +99,6 @@ public void close() throws Exception { } } - /** Formatter which is equal to itself, but not to any other Formatter. */ - static class NeverUpToDate extends FormatterStepImpl { - private static final long serialVersionUID = 1L; - - private static final Random RANDOM = new Random(); - - final transient ThrowingEx.Supplier formatterSupplier; - transient FormatterFunc formatter; // initialized lazily - - NeverUpToDate(String name, ThrowingEx.Supplier formatterSupplier) { - super(name, RANDOM::nextInt); - this.formatterSupplier = Objects.requireNonNull(formatterSupplier, "formatterSupplier"); - } - - @Override - protected String format(Integer state, String rawUnix, File file) throws Exception { - if (formatter == null) { - formatter = formatterSupplier.get(); - if (formatter instanceof FormatterFunc.Closeable) { - throw new AssertionError("NeverUpToDate does not support FormatterFunc.Closeable. See https://github.com/diffplug/spotless/pull/284"); - } - } - return formatter.apply(rawUnix, file); - } - - @Override - public void close() throws Exception { - if (formatter instanceof FormatterFunc.Closeable) { - ((FormatterFunc.Closeable) formatter).close(); - formatter = null; - } - } - } - static void checkNotSentinel(File file) { if (file == Formatter.NO_FILE_SENTINEL) { throw new IllegalArgumentException("This step requires the underlying file. If this is a test, use StepHarnessWithFile"); diff --git a/lib/src/main/java/com/diffplug/spotless/NeverUpToDateStep.java b/lib/src/main/java/com/diffplug/spotless/NeverUpToDateStep.java new file mode 100644 index 0000000000..e54247e37f --- /dev/null +++ b/lib/src/main/java/com/diffplug/spotless/NeverUpToDateStep.java @@ -0,0 +1,56 @@ +/* + * Copyright 2024 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; + +import java.io.File; +import java.util.Objects; +import java.util.Random; + +/** + * Formatter which is equal to itself, but not to any other Formatter. + */ +class NeverUpToDateStep extends FormatterStepImpl { + private static final long serialVersionUID = 1L; + + private static final Random RANDOM = new Random(); + + final transient ThrowingEx.Supplier formatterSupplier; + transient FormatterFunc formatter; // initialized lazily + + NeverUpToDateStep(String name, ThrowingEx.Supplier formatterSupplier) { + super(name, RANDOM::nextInt); + this.formatterSupplier = Objects.requireNonNull(formatterSupplier, "formatterSupplier"); + } + + @Override + protected String format(Integer state, String rawUnix, File file) throws Exception { + if (formatter == null) { + formatter = formatterSupplier.get(); + if (formatter instanceof FormatterFunc.Closeable) { + throw new AssertionError("NeverUpToDate does not support FormatterFunc.Closeable. See https://github.com/diffplug/spotless/pull/284"); + } + } + return formatter.apply(rawUnix, file); + } + + @Override + public void close() throws Exception { + if (formatter instanceof FormatterFunc.Closeable) { + ((FormatterFunc.Closeable) formatter).close(); + formatter = null; + } + } +} From 7c0450d5681bb2eb57512236615e54ba98be65ae Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Sun, 7 Apr 2024 16:23:28 -0700 Subject: [PATCH 3/9] NeverUpToDateStep is simpler to implement without `FormatterStepImpl`. --- .../diffplug/spotless/NeverUpToDateStep.java | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/lib/src/main/java/com/diffplug/spotless/NeverUpToDateStep.java b/lib/src/main/java/com/diffplug/spotless/NeverUpToDateStep.java index e54247e37f..f3bd76e2d6 100644 --- a/lib/src/main/java/com/diffplug/spotless/NeverUpToDateStep.java +++ b/lib/src/main/java/com/diffplug/spotless/NeverUpToDateStep.java @@ -17,26 +17,29 @@ import java.io.File; import java.util.Objects; -import java.util.Random; /** * Formatter which is equal to itself, but not to any other Formatter. */ -class NeverUpToDateStep extends FormatterStepImpl { +class NeverUpToDateStep implements FormatterStep { private static final long serialVersionUID = 1L; - private static final Random RANDOM = new Random(); - - final transient ThrowingEx.Supplier formatterSupplier; - transient FormatterFunc formatter; // initialized lazily + private final String name; + private final ThrowingEx.Supplier formatterSupplier; + private transient FormatterFunc formatter; // initialized lazily NeverUpToDateStep(String name, ThrowingEx.Supplier formatterSupplier) { - super(name, RANDOM::nextInt); + this.name = name; this.formatterSupplier = Objects.requireNonNull(formatterSupplier, "formatterSupplier"); } @Override - protected String format(Integer state, String rawUnix, File file) throws Exception { + public String getName() { + return name; + } + + @Override + public String format(String rawUnix, File file) throws Exception { if (formatter == null) { formatter = formatterSupplier.get(); if (formatter instanceof FormatterFunc.Closeable) { From 83419889c26386b865c60ef9c137b56f90f9faee Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Thu, 23 May 2024 16:06:45 -0700 Subject: [PATCH 4/9] Convenience method for FileSignature of a single file. --- lib/src/main/java/com/diffplug/spotless/FileSignature.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/src/main/java/com/diffplug/spotless/FileSignature.java b/lib/src/main/java/com/diffplug/spotless/FileSignature.java index 10bba7a716..e5d495b9bd 100644 --- a/lib/src/main/java/com/diffplug/spotless/FileSignature.java +++ b/lib/src/main/java/com/diffplug/spotless/FileSignature.java @@ -125,6 +125,10 @@ public static Promised promise(Iterable files) { return new Promised(MoreIterables.toNullHostileList(files), null); } + public static Promised promise(File file) { + return new Promised(MoreIterables.toNullHostileList(Collections.singletonList(file)), null); + } + /** Returns all of the files in this signature, throwing an exception if there are more or less than 1 file. */ public Collection files() { return Collections.unmodifiableList(files); From 401d595f93e241c0bb4da73cf29b8da883932e8a Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Thu, 23 May 2024 16:54:43 -0700 Subject: [PATCH 5/9] Use the round-tripped step, not the pre-roundtripped step. --- .../src/main/java/com/diffplug/spotless/StepHarnessBase.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/testlib/src/main/java/com/diffplug/spotless/StepHarnessBase.java b/testlib/src/main/java/com/diffplug/spotless/StepHarnessBase.java index 656057cd4e..d86021ef99 100644 --- a/testlib/src/main/java/com/diffplug/spotless/StepHarnessBase.java +++ b/testlib/src/main/java/com/diffplug/spotless/StepHarnessBase.java @@ -27,14 +27,15 @@ enum RoundTrip { private final Formatter formatter; protected StepHarnessBase(Formatter formatter, RoundTrip roundTrip) { - this.formatter = Objects.requireNonNull(formatter); if (roundTrip == RoundTrip.DONT_ROUNDTRIP) { + this.formatter = Objects.requireNonNull(formatter); return; } Formatter roundTripped = SerializableEqualityTester.reserialize(formatter); if (roundTrip == RoundTrip.ASSERT_EQUAL) { Assertions.assertThat(roundTripped).isEqualTo(formatter); } + this.formatter = roundTripped; } protected Formatter formatter() { From afa4b3d045ac1dd8d477b85f4bfe0b9e5cfedd1b Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Thu, 23 May 2024 16:06:11 -0700 Subject: [PATCH 6/9] Fix round-tripping of NativeCmdStep. --- .../spotless/generic/NativeCmdStep.java | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/lib/src/main/java/com/diffplug/spotless/generic/NativeCmdStep.java b/lib/src/main/java/com/diffplug/spotless/generic/NativeCmdStep.java index 31b30fe56f..3e58c9dbae 100644 --- a/lib/src/main/java/com/diffplug/spotless/generic/NativeCmdStep.java +++ b/lib/src/main/java/com/diffplug/spotless/generic/NativeCmdStep.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 DiffPlug + * Copyright 2021-2024 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,24 +35,37 @@ private NativeCmdStep() {} public static FormatterStep create(String name, File pathToExe, List arguments) { Objects.requireNonNull(name, "name"); Objects.requireNonNull(pathToExe, "pathToExe"); - return FormatterStep.createLazy(name, () -> new State(FileSignature.signAsList(pathToExe), arguments), State::toFunc); + return FormatterStep.createLazy(name, () -> new State(FileSignature.promise(pathToExe), arguments), State::toRuntime, Runtime::toFunc); } static class State implements Serializable { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 2L; + final FileSignature.Promised pathToExe; + final List arguments; - final FileSignature pathToExe; + State(FileSignature.Promised pathToExe, List arguments) { + this.pathToExe = pathToExe; + this.arguments = arguments; + } + + Runtime toRuntime() { + return new Runtime(pathToExe.get().getOnlyFile(), arguments); + } + } + static class Runtime implements Serializable { + private static final long serialVersionUID = 2L; + final File pathToExe; final List arguments; - State(FileSignature pathToExe, List arguments) { + Runtime(File pathToExe, List arguments) { this.pathToExe = pathToExe; this.arguments = arguments; } String format(ProcessRunner runner, String input) throws IOException, InterruptedException { List argumentsWithPathToExe = new ArrayList<>(); - argumentsWithPathToExe.add(pathToExe.getOnlyFile().getAbsolutePath()); + argumentsWithPathToExe.add(pathToExe.getAbsolutePath()); if (arguments != null) { argumentsWithPathToExe.addAll(arguments); } From b03766e1f02802d8f2e93f2ef955b29901788f0d Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Thu, 4 Apr 2024 10:35:37 -0700 Subject: [PATCH 7/9] Fix round-tripping of DBeaver step. --- .../spotless/sql/DBeaverSQLFormatterStep.java | 27 ++++++------------- .../sql/DBeaverSQLFormatterStepTest.java | 10 +++---- 2 files changed, 13 insertions(+), 24 deletions(-) diff --git a/lib/src/main/java/com/diffplug/spotless/sql/DBeaverSQLFormatterStep.java b/lib/src/main/java/com/diffplug/spotless/sql/DBeaverSQLFormatterStep.java index fece2ea0fd..e9c7a59e54 100644 --- a/lib/src/main/java/com/diffplug/spotless/sql/DBeaverSQLFormatterStep.java +++ b/lib/src/main/java/com/diffplug/spotless/sql/DBeaverSQLFormatterStep.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 DiffPlug + * Copyright 2016-2024 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,6 @@ package com.diffplug.spotless.sql; import java.io.File; -import java.io.Serializable; import com.diffplug.spotless.FileSignature; import com.diffplug.spotless.FormatterFunc; @@ -32,24 +31,14 @@ public class DBeaverSQLFormatterStep { private DBeaverSQLFormatterStep() {} public static FormatterStep create(Iterable files) { - return FormatterStep.createLazy(NAME, - () -> new State(files), - State::createFormat); + return FormatterStep.create(NAME, FileSignature.promise(files), + FileSignature.Promised::get, + DBeaverSQLFormatterStep::createFormat); } - static final class State implements Serializable { - private static final long serialVersionUID = 1L; - - final FileSignature settingsSignature; - - State(final Iterable settingsFiles) throws Exception { - this.settingsSignature = FileSignature.signAsList(settingsFiles); - } - - FormatterFunc createFormat() throws Exception { - FormatterProperties preferences = FormatterProperties.from(settingsSignature.files()); - DBeaverSQLFormatter dbeaverSqlFormatter = new DBeaverSQLFormatter(preferences.getProperties()); - return dbeaverSqlFormatter::format; - } + private static FormatterFunc createFormat(FileSignature settings) { + FormatterProperties preferences = FormatterProperties.from(settings.files()); + DBeaverSQLFormatter dbeaverSqlFormatter = new DBeaverSQLFormatter(preferences.getProperties()); + return dbeaverSqlFormatter::format; } } 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 58b0115b9c..648ddc6669 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-2021 DiffPlug + * Copyright 2016-2024 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,7 +29,7 @@ class DBeaverSQLFormatterStepTest extends ResourceHarness { @Test - void behavior() throws Exception { + void behavior() { FormatterStep step = DBeaverSQLFormatterStep.create(Collections.emptySet()); StepHarness.forStep(step) .testResource("sql/dbeaver/full.dirty", "sql/dbeaver/full.clean") @@ -40,21 +40,21 @@ void behavior() throws Exception { } @Test - void behaviorWithConfigFile() throws Exception { + void behaviorWithConfigFile() { FormatterStep step = DBeaverSQLFormatterStep.create(createTestFiles("sql/dbeaver/sqlConfig.properties")); StepHarness.forStep(step) .testResource("sql/dbeaver/create.dirty", "sql/dbeaver/create.clean"); } @Test - void behaviorWithAlternativeConfigFile() throws Exception { + void behaviorWithAlternativeConfigFile() { FormatterStep step = DBeaverSQLFormatterStep.create(createTestFiles("sql/dbeaver/sqlConfig2.properties")); StepHarness.forStep(step) .testResource("sql/dbeaver/create.dirty", "sql/dbeaver/create.clean.alternative"); } @Test - void equality() throws Exception { + void equality() { List sqlConfig1 = createTestFiles("sql/dbeaver/sqlConfig.properties"); List sqlConfig2 = createTestFiles("sql/dbeaver/sqlConfig2.properties"); new SerializableEqualityTester() { From 226dbc735432d6f46e982d9bd004afb1a5869fdd Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Thu, 23 May 2024 16:19:42 -0700 Subject: [PATCH 8/9] Fixup FenceStepTest so that we don't need so much `forStepNoRoundtrip`. --- .../spotless/generic/FenceStepTest.java | 70 +++++++++---------- 1 file changed, 32 insertions(+), 38 deletions(-) diff --git a/testlib/src/test/java/com/diffplug/spotless/generic/FenceStepTest.java b/testlib/src/test/java/com/diffplug/spotless/generic/FenceStepTest.java index 682855755e..697a199fd2 100644 --- a/testlib/src/test/java/com/diffplug/spotless/generic/FenceStepTest.java +++ b/testlib/src/test/java/com/diffplug/spotless/generic/FenceStepTest.java @@ -16,16 +16,11 @@ package com.diffplug.spotless.generic; import java.io.File; -import java.io.Serializable; import java.util.Arrays; -import java.util.Objects; - -import javax.annotation.Nullable; import org.junit.jupiter.api.Test; import com.diffplug.common.base.StringPrinter; -import com.diffplug.spotless.FormatterFunc; import com.diffplug.spotless.FormatterStep; import com.diffplug.spotless.ResourceHarness; import com.diffplug.spotless.StepHarness; @@ -34,8 +29,8 @@ class FenceStepTest extends ResourceHarness { @Test void single() { FormatterStep fence = FenceStep.named("fence").openClose("spotless:off", "spotless:on") - .preserveWithin(Arrays.asList(createNeverUpToDateSerializable("lowercase", String::toLowerCase))); - StepHarness harness = StepHarness.forStepNoRoundtrip(fence); + .preserveWithin(Arrays.asList(ToCaseStep.lower())); + StepHarness harness = StepHarness.forStep(fence); harness.test( StringPrinter.buildStringFromLines( "A B C", @@ -54,8 +49,8 @@ void single() { @Test void multiple() { FormatterStep fence = FenceStep.named("fence").openClose("spotless:off", "spotless:on") - .preserveWithin(Arrays.asList(createNeverUpToDateSerializable("lowercase", String::toLowerCase))); - StepHarness harness = StepHarness.forStepNoRoundtrip(fence); + .preserveWithin(Arrays.asList(ToCaseStep.lower())); + StepHarness harness = StepHarness.forStep(fence); harness.test( StringPrinter.buildStringFromLines( "A B C", @@ -88,7 +83,7 @@ void multiple() { @Test void broken() { FormatterStep fence = FenceStep.named("fence").openClose("spotless:off", "spotless:on") - .preserveWithin(Arrays.asList(createNeverUpToDateSerializable("uppercase", String::toUpperCase))); + .preserveWithin(Arrays.asList(ToCaseStep.upper())); // this fails because uppercase turns spotless:off into SPOTLESS:OFF, etc StepHarness.forStepNoRoundtrip(fence).testExceptionMsg(StringPrinter.buildStringFromLines("A B C", "spotless:off", @@ -100,8 +95,8 @@ void broken() { @Test void andApply() { FormatterStep fence = FenceStep.named("fence").openClose("", "") - .applyWithin(Arrays.asList(createNeverUpToDateSerializable("lowercase", String::toLowerCase))); - StepHarness.forStepNoRoundtrip(fence).test( + .applyWithin(Arrays.asList(ToCaseStep.lower())); + StepHarness.forStep(fence).test( StringPrinter.buildStringFromLines( "A B C", "", @@ -116,46 +111,45 @@ void andApply() { "G H I")); } - /** - * @param name - * The name of the formatter step - * @param function - * The function used by the formatter step - * @return A FormatterStep which will never report that it is up-to-date, because - * it is not equal to the serialized representation of itself. - */ - static FormatterStep createNeverUpToDateSerializable( - String name, - T function) { - Objects.requireNonNull(function, "function"); - return new NeverUpToDateSerializable(name, function); - } + static class ToCaseStep implements FormatterStep { + static ToCaseStep upper() { + return new ToCaseStep(true); + } - static class NeverUpToDateSerializable implements FormatterStep, Serializable { - private final String name; - private final T formatterFunc; + static ToCaseStep lower() { + return new ToCaseStep(false); + } + + private final boolean uppercase; - private NeverUpToDateSerializable(String name, T formatterFunc) { - this.name = name; - this.formatterFunc = formatterFunc; + ToCaseStep(boolean uppercase) { + this.uppercase = uppercase; } @Override public String getName() { - return name; + return uppercase ? "uppercase" : "lowercase"; } - @Nullable + @org.jetbrains.annotations.Nullable @Override public String format(String rawUnix, File file) throws Exception { - return formatterFunc.apply(rawUnix, file); + return uppercase ? rawUnix.toUpperCase() : rawUnix.toLowerCase(); } @Override public void close() throws Exception { - if (formatterFunc instanceof FormatterFunc.Closeable) { - ((FormatterFunc.Closeable) formatterFunc).close(); - } + + } + + @Override + public int hashCode() { + return getName().hashCode(); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof ToCaseStep && getName().equals(((ToCaseStep) obj).getName()); } } } From 27657ec5020033bd493554eb71ffadcddf078514 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Thu, 23 May 2024 17:21:28 -0700 Subject: [PATCH 9/9] Remove `FormatterStepImpl` completely, route all steps through `FormatterStepEqualityOnStateSerialization`. --- .../java/com/diffplug/spotless/Formatter.java | 6 + .../com/diffplug/spotless/FormatterFunc.java | 4 +- .../com/diffplug/spotless/FormatterStep.java | 6 +- .../diffplug/spotless/FormatterStepImpl.java | 107 ------------------ 4 files changed, 11 insertions(+), 112 deletions(-) delete mode 100644 lib/src/main/java/com/diffplug/spotless/FormatterStepImpl.java diff --git a/lib/src/main/java/com/diffplug/spotless/Formatter.java b/lib/src/main/java/com/diffplug/spotless/Formatter.java index 9400989ddf..b5cd5f81fc 100644 --- a/lib/src/main/java/com/diffplug/spotless/Formatter.java +++ b/lib/src/main/java/com/diffplug/spotless/Formatter.java @@ -312,4 +312,10 @@ public void close() { /** This Sentinel reference may be used to pass string content to a Formatter or FormatterStep when there is no actual File to format */ public static final File NO_FILE_SENTINEL = new File("NO_FILE_SENTINEL"); + + static void checkNotSentinel(File file) { + if (file == Formatter.NO_FILE_SENTINEL) { + throw new IllegalArgumentException("This step requires the underlying file. If this is a test, use StepHarnessWithFile"); + } + } } diff --git a/lib/src/main/java/com/diffplug/spotless/FormatterFunc.java b/lib/src/main/java/com/diffplug/spotless/FormatterFunc.java index 7aee828b0d..800a553225 100644 --- a/lib/src/main/java/com/diffplug/spotless/FormatterFunc.java +++ b/lib/src/main/java/com/diffplug/spotless/FormatterFunc.java @@ -115,7 +115,7 @@ public void close() { @Override public String apply(String unix, File file) throws Exception { - FormatterStepImpl.checkNotSentinel(file); + Formatter.checkNotSentinel(file); return function.apply(resource, unix, file); } @@ -144,7 +144,7 @@ interface NeedsFile extends FormatterFunc { @Override default String apply(String unix, File file) throws Exception { - FormatterStepImpl.checkNotSentinel(file); + Formatter.checkNotSentinel(file); return applyWithFile(unix, file); } diff --git a/lib/src/main/java/com/diffplug/spotless/FormatterStep.java b/lib/src/main/java/com/diffplug/spotless/FormatterStep.java index 567712eaab..0fc98c0686 100644 --- a/lib/src/main/java/com/diffplug/spotless/FormatterStep.java +++ b/lib/src/main/java/com/diffplug/spotless/FormatterStep.java @@ -129,8 +129,8 @@ static static FormatterStep createLazy( String name, ThrowingEx.Supplier stateSupplier, - ThrowingEx.Function stateToFormatter) { - return new FormatterStepImpl.Standard<>(name, stateSupplier, stateToFormatter); + SerializedFunction stateToFormatter) { + return createLazy(name, stateSupplier, SerializedFunction.identity(), stateToFormatter); } /** @@ -146,7 +146,7 @@ static FormatterStep createLazy( static FormatterStep create( String name, State state, - ThrowingEx.Function stateToFormatter) { + SerializedFunction stateToFormatter) { Objects.requireNonNull(state, "state"); return createLazy(name, () -> state, stateToFormatter); } diff --git a/lib/src/main/java/com/diffplug/spotless/FormatterStepImpl.java b/lib/src/main/java/com/diffplug/spotless/FormatterStepImpl.java deleted file mode 100644 index 14b04a178d..0000000000 --- a/lib/src/main/java/com/diffplug/spotless/FormatterStepImpl.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2016-2024 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; - -import java.io.File; -import java.io.Serializable; -import java.util.Objects; - -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; - -/** - * Standard implementation of FormatExtension which cleanly enforces - * separation of serializable configuration and a pure format function. - *

- * Not an inner-class of FormatterStep so that it can stay entirely private - * from the API. - */ -@SuppressFBWarnings("SE_TRANSIENT_FIELD_NOT_RESTORED") -abstract class FormatterStepImpl extends LazyForwardingEquality implements FormatterStep { - private static final long serialVersionUID = 2L; - - /** Transient because only the state matters. */ - final transient String name; - - /** Transient because only the state matters. */ - transient ThrowingEx.Supplier stateSupplier; - - FormatterStepImpl(String name, ThrowingEx.Supplier stateSupplier) { - this.name = Objects.requireNonNull(name); - this.stateSupplier = Objects.requireNonNull(stateSupplier); - } - - @Override - public String getName() { - return name; - } - - /** - * Implements the formatting function strictly in terms - * of the input data and the result of {@link #calculateState()}. - */ - protected abstract String format(State state, String rawUnix, File file) throws Exception; - - @Override - public final String format(String rawUnix, File file) throws Exception { - return format(state(), rawUnix, file); - } - - @Override - protected State calculateState() throws Exception { - // LazyForwardingEquality guarantees that this will only be called once, and keeping toFormat - // causes a memory leak, see https://github.com/diffplug/spotless/issues/1194 - State state = stateSupplier.get(); - stateSupplier = null; - return state; - } - - static final class Standard extends FormatterStepImpl { - private static final long serialVersionUID = 1L; - - final transient ThrowingEx.Function stateToFormatter; - transient FormatterFunc formatter; // initialized lazily - - Standard(String name, ThrowingEx.Supplier stateSupplier, ThrowingEx.Function stateToFormatter) { - super(name, stateSupplier); - this.stateToFormatter = Objects.requireNonNull(stateToFormatter); - } - - @Override - protected String format(State state, String rawUnix, File file) throws Exception { - Objects.requireNonNull(state, "state"); - Objects.requireNonNull(rawUnix, "rawUnix"); - Objects.requireNonNull(file, "file"); - if (formatter == null) { - formatter = stateToFormatter.apply(state()); - } - return formatter.apply(rawUnix, file); - } - - @Override - public void close() throws Exception { - if (formatter instanceof FormatterFunc.Closeable) { - ((FormatterFunc.Closeable) formatter).close(); - formatter = null; - } - } - } - - static void checkNotSentinel(File file) { - if (file == Formatter.NO_FILE_SENTINEL) { - throw new IllegalArgumentException("This step requires the underlying file. If this is a test, use StepHarnessWithFile"); - } - } -}