Skip to content

Commit fe898e8

Browse files
committed
Moved PaddedCell.DirtyState to its own top-level class with new methods.
1 parent b3462c2 commit fe898e8

File tree

7 files changed

+160
-127
lines changed

7 files changed

+160
-127
lines changed

CHANGES.md

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
1212
## [TBD lint release]
1313
* **BREAKING** Removed `isClean`, `applyTo`, and `applyToAndReturnResultIfDirty` from `Formatter` because users should instead use `PaddedCell.check()`.
1414
* **BREAKING** Removed `FormatterStep.Strict` because it was unnecessary and unused implementation detail.
15+
* **BREAKING** Moved `PaddedCell.DirtyState` to its own top-level class with new methods.
1516

1617
## [Unreleased]
1718
### Changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/*
2+
* Copyright 2022 DiffPlug
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.diffplug.spotless;
17+
18+
import java.io.File;
19+
import java.io.IOException;
20+
import java.io.OutputStream;
21+
import java.nio.file.Files;
22+
import java.util.Arrays;
23+
24+
import javax.annotation.Nullable;
25+
26+
/**
27+
* The clean/dirty state of a single file. Intended use:
28+
* - {@link #isClean()} means that the file is is clean, and there's nothing else to say
29+
* - {@link #didNotConverge()} means that we were unable to determine a clean state
30+
* - once you've tested the above conditions and you know that it's a dirty file with a converged state,
31+
* then you can call {@link #writeCanonicalTo(OutputStream)} to get the canonical form of the given file.
32+
*/
33+
public class DirtyState {
34+
@Nullable
35+
private final byte[] canonicalBytes;
36+
37+
DirtyState(@Nullable byte[] canonicalBytes) {
38+
this.canonicalBytes = canonicalBytes;
39+
}
40+
41+
public boolean isClean() {
42+
return this == isClean;
43+
}
44+
45+
public boolean didNotConverge() {
46+
return this == didNotConverge;
47+
}
48+
49+
private byte[] canonicalBytes() {
50+
if (canonicalBytes == null) {
51+
throw new IllegalStateException("First make sure that {@code !isClean()} and {@code !didNotConverge()}");
52+
}
53+
return canonicalBytes;
54+
}
55+
56+
public void writeCanonicalTo(File file) throws IOException {
57+
Files.write(file.toPath(), canonicalBytes());
58+
}
59+
60+
public void writeCanonicalTo(OutputStream out) throws IOException {
61+
out.write(canonicalBytes());
62+
}
63+
64+
/** Returns the DirtyState which corresponds to {@code isClean()}. */
65+
public static DirtyState clean() {
66+
return isClean;
67+
}
68+
69+
static final DirtyState didNotConverge = new DirtyState(null);
70+
static final DirtyState isClean = new DirtyState(null);
71+
72+
public static Calculation of(Formatter formatter, File file) throws IOException {
73+
return of(formatter, file, Files.readAllBytes(file.toPath()));
74+
}
75+
76+
public static Calculation of(Formatter formatter, File file, byte[] rawBytes) {
77+
return new Calculation(formatter, file, rawBytes);
78+
}
79+
80+
public static class Calculation {
81+
private final Formatter formatter;
82+
private final File file;
83+
private final byte[] rawBytes;
84+
private final String raw;
85+
86+
private Calculation(Formatter formatter, File file, byte[] rawBytes) {
87+
this.formatter = formatter;
88+
this.file = file;
89+
this.rawBytes = rawBytes;
90+
this.raw = new String(rawBytes, formatter.getEncoding());
91+
// check that all characters were encodable
92+
String encodingError = EncodingErrorMsg.msg(raw, rawBytes, formatter.getEncoding());
93+
if (encodingError != null) {
94+
throw new IllegalArgumentException(encodingError);
95+
}
96+
}
97+
98+
/**
99+
* Calculates whether the given file is dirty according to a PaddedCell invocation of the given formatter.
100+
* DirtyState includes the clean state of the file, as well as a warning if we were not able to apply the formatter
101+
* due to diverging idempotence.
102+
*/
103+
public DirtyState calculateDirtyState() {
104+
String rawUnix = LineEnding.toUnix(raw);
105+
106+
// enforce the format
107+
String formattedUnix = formatter.compute(rawUnix, file);
108+
// convert the line endings if necessary
109+
String formatted = formatter.computeLineEndings(formattedUnix, file);
110+
111+
// if F(input) == input, then the formatter is well-behaving and the input is clean
112+
byte[] formattedBytes = formatted.getBytes(formatter.getEncoding());
113+
if (Arrays.equals(rawBytes, formattedBytes)) {
114+
return isClean;
115+
}
116+
117+
// F(input) != input, so we'll do a padded check
118+
String doubleFormattedUnix = formatter.compute(formattedUnix, file);
119+
if (doubleFormattedUnix.equals(formattedUnix)) {
120+
// most dirty files are idempotent-dirty, so this is a quick-short circuit for that common case
121+
return new DirtyState(formattedBytes);
122+
}
123+
124+
PaddedCell cell = PaddedCell.check(formatter, file, rawUnix);
125+
if (!cell.isResolvable()) {
126+
return didNotConverge;
127+
}
128+
129+
// get the canonical bytes
130+
String canonicalUnix = cell.canonical();
131+
String canonical = formatter.computeLineEndings(canonicalUnix, file);
132+
byte[] canonicalBytes = canonical.getBytes(formatter.getEncoding());
133+
if (!Arrays.equals(rawBytes, canonicalBytes)) {
134+
// and write them to disk if needed
135+
return new DirtyState(canonicalBytes);
136+
} else {
137+
return isClean;
138+
}
139+
}
140+
}
141+
}

lib/src/main/java/com/diffplug/spotless/PaddedCell.java

+1-110
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2021 DiffPlug
2+
* Copyright 2016-2022 DiffPlug
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,19 +18,14 @@
1818
import static com.diffplug.spotless.LibPreconditions.requireElementsNonNull;
1919

2020
import java.io.File;
21-
import java.io.IOException;
22-
import java.io.OutputStream;
2321
import java.nio.file.Files;
2422
import java.util.ArrayList;
25-
import java.util.Arrays;
2623
import java.util.Collections;
2724
import java.util.Comparator;
2825
import java.util.List;
2926
import java.util.Objects;
3027
import java.util.function.Function;
3128

32-
import javax.annotation.Nullable;
33-
3429
/**
3530
* Models the result of applying a {@link Formatter} on a given {@link File}
3631
* while characterizing various failure modes (slow convergence, cycles, and divergence).
@@ -176,108 +171,4 @@ public String userMessage() {
176171
}
177172
// @formatter:on
178173
}
179-
180-
/**
181-
* Calculates whether the given file is dirty according to a PaddedCell invocation of the given formatter.
182-
* DirtyState includes the clean state of the file, as well as a warning if we were not able to apply the formatter
183-
* due to diverging idempotence.
184-
*/
185-
public static DirtyState calculateDirtyState(Formatter formatter, File file) throws IOException {
186-
Objects.requireNonNull(formatter, "formatter");
187-
Objects.requireNonNull(file, "file");
188-
189-
byte[] rawBytes = Files.readAllBytes(file.toPath());
190-
return calculateDirtyState(formatter, file, rawBytes);
191-
}
192-
193-
public static DirtyState calculateDirtyState(Formatter formatter, File file, byte[] rawBytes) throws IOException {
194-
String raw = new String(rawBytes, formatter.getEncoding());
195-
// check that all characters were encodable
196-
String encodingError = EncodingErrorMsg.msg(raw, rawBytes, formatter.getEncoding());
197-
if (encodingError != null) {
198-
throw new IllegalArgumentException(encodingError);
199-
}
200-
String rawUnix = LineEnding.toUnix(raw);
201-
202-
// enforce the format
203-
String formattedUnix = formatter.compute(rawUnix, file);
204-
// convert the line endings if necessary
205-
String formatted = formatter.computeLineEndings(formattedUnix, file);
206-
207-
// if F(input) == input, then the formatter is well-behaving and the input is clean
208-
byte[] formattedBytes = formatted.getBytes(formatter.getEncoding());
209-
if (Arrays.equals(rawBytes, formattedBytes)) {
210-
return isClean;
211-
}
212-
213-
// F(input) != input, so we'll do a padded check
214-
String doubleFormattedUnix = formatter.compute(formattedUnix, file);
215-
if (doubleFormattedUnix.equals(formattedUnix)) {
216-
// most dirty files are idempotent-dirty, so this is a quick-short circuit for that common case
217-
return new DirtyState(formattedBytes);
218-
}
219-
220-
PaddedCell cell = PaddedCell.check(formatter, file, rawUnix);
221-
if (!cell.isResolvable()) {
222-
return didNotConverge;
223-
}
224-
225-
// get the canonical bytes
226-
String canonicalUnix = cell.canonical();
227-
String canonical = formatter.computeLineEndings(canonicalUnix, file);
228-
byte[] canonicalBytes = canonical.getBytes(formatter.getEncoding());
229-
if (!Arrays.equals(rawBytes, canonicalBytes)) {
230-
// and write them to disk if needed
231-
return new DirtyState(canonicalBytes);
232-
} else {
233-
return isClean;
234-
}
235-
}
236-
237-
/**
238-
* The clean/dirty state of a single file. Intended use:
239-
* - {@link #isClean()} means that the file is is clean, and there's nothing else to say
240-
* - {@link #didNotConverge()} means that we were unable to determine a clean state
241-
* - once you've tested the above conditions and you know that it's a dirty file with a converged state,
242-
* then you can call {@link #writeCanonicalTo(OutputStream)} to get the canonical form of the given file.
243-
*/
244-
public static class DirtyState {
245-
@Nullable
246-
private final byte[] canonicalBytes;
247-
248-
private DirtyState(@Nullable byte[] canonicalBytes) {
249-
this.canonicalBytes = canonicalBytes;
250-
}
251-
252-
public boolean isClean() {
253-
return this == isClean;
254-
}
255-
256-
public boolean didNotConverge() {
257-
return this == didNotConverge;
258-
}
259-
260-
private byte[] canonicalBytes() {
261-
if (canonicalBytes == null) {
262-
throw new IllegalStateException("First make sure that {@code !isClean()} and {@code !didNotConverge()}");
263-
}
264-
return canonicalBytes;
265-
}
266-
267-
public void writeCanonicalTo(File file) throws IOException {
268-
Files.write(file.toPath(), canonicalBytes());
269-
}
270-
271-
public void writeCanonicalTo(OutputStream out) throws IOException {
272-
out.write(canonicalBytes());
273-
}
274-
}
275-
276-
/** Returns the DirtyState which corresponds to {@code isClean()}. */
277-
public static DirtyState isClean() {
278-
return isClean;
279-
}
280-
281-
private static final DirtyState didNotConverge = new DirtyState(null);
282-
private static final DirtyState isClean = new DirtyState(null);
283174
}

plugin-gradle/src/main/java/com/diffplug/gradle/spotless/IdeHook.java

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2021 DiffPlug
2+
* Copyright 2016-2022 DiffPlug
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,12 +17,11 @@
1717

1818
import java.io.File;
1919
import java.io.IOException;
20-
import java.nio.file.Files;
2120

2221
import com.diffplug.common.base.Errors;
2322
import com.diffplug.common.io.ByteStreams;
23+
import com.diffplug.spotless.DirtyState;
2424
import com.diffplug.spotless.Formatter;
25-
import com.diffplug.spotless.PaddedCell;
2625

2726
class IdeHook {
2827
final static String PROPERTY = "spotlessIdeHook";
@@ -49,13 +48,14 @@ static void performHook(SpotlessTaskImpl spotlessTask) {
4948
return;
5049
}
5150
}
51+
DirtyState.Calculation init;
5252
byte[] bytes;
5353
if (spotlessTask.getProject().hasProperty(USE_STD_IN)) {
54-
bytes = ByteStreams.toByteArray(System.in);
54+
init = DirtyState.of(formatter, file, ByteStreams.toByteArray(System.in));
5555
} else {
56-
bytes = Files.readAllBytes(file.toPath());
56+
init = DirtyState.of(formatter, file);
5757
}
58-
PaddedCell.DirtyState dirty = PaddedCell.calculateDirtyState(formatter, file, bytes);
58+
DirtyState dirty = init.calculateDirtyState();
5959
if (dirty.isClean()) {
6060
dumpIsClean();
6161
} else if (dirty.didNotConverge()) {

plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessTaskImpl.java

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2021 DiffPlug
2+
* Copyright 2016-2022 DiffPlug
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -36,8 +36,8 @@
3636
import org.gradle.work.InputChanges;
3737

3838
import com.diffplug.common.base.StringPrinter;
39+
import com.diffplug.spotless.DirtyState;
3940
import com.diffplug.spotless.Formatter;
40-
import com.diffplug.spotless.PaddedCell;
4141
import com.diffplug.spotless.extra.GitRatchet;
4242

4343
@CacheableTask
@@ -85,11 +85,11 @@ public void performAction(InputChanges inputs) throws Exception {
8585
private void processInputFile(@Nullable GitRatchet ratchet, Formatter formatter, File input) throws IOException {
8686
File output = getOutputFile(input);
8787
getLogger().debug("Applying format to " + input + " and writing to " + output);
88-
PaddedCell.DirtyState dirtyState;
88+
DirtyState dirtyState;
8989
if (ratchet != null && ratchet.isClean(getProjectDir().get().getAsFile(), getRootTreeSha(), input)) {
90-
dirtyState = PaddedCell.isClean();
90+
dirtyState = DirtyState.clean();
9191
} else {
92-
dirtyState = PaddedCell.calculateDirtyState(formatter, input);
92+
dirtyState = DirtyState.of(formatter, input).calculateDirtyState();
9393
}
9494
if (dirtyState.isClean()) {
9595
// Remove previous output if it exists

plugin-maven/src/main/java/com/diffplug/spotless/maven/SpotlessApplyMojo.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2021 DiffPlug
2+
* Copyright 2016-2022 DiffPlug
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -21,8 +21,8 @@
2121
import org.apache.maven.plugin.MojoExecutionException;
2222
import org.apache.maven.plugins.annotations.Mojo;
2323

24+
import com.diffplug.spotless.DirtyState;
2425
import com.diffplug.spotless.Formatter;
25-
import com.diffplug.spotless.PaddedCell;
2626
import com.diffplug.spotless.maven.incremental.UpToDateChecker;
2727

2828
/**
@@ -42,7 +42,7 @@ protected void process(Iterable<File> files, Formatter formatter, UpToDateChecke
4242
}
4343

4444
try {
45-
PaddedCell.DirtyState dirtyState = PaddedCell.calculateDirtyState(formatter, file);
45+
DirtyState dirtyState = DirtyState.of(formatter, file).calculateDirtyState();
4646
if (!dirtyState.isClean() && !dirtyState.didNotConverge()) {
4747
dirtyState.writeCanonicalTo(file);
4848
}

0 commit comments

Comments
 (0)