Skip to content

Commit 47985cd

Browse files
authored
Add Formatter.NO_FILE_SENTINEL (#1525)
2 parents 5bef415 + 1f78aff commit 47985cd

File tree

9 files changed

+121
-17
lines changed

9 files changed

+121
-17
lines changed

CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ This document is intended for Spotless developers.
1010
We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `1.27.0`).
1111

1212
## [Unreleased]
13+
### Added
14+
* `Formatter` now has a field `public static final File NO_FILE_SENTINEL` which can be used to pass string content to a Formatter or FormatterStep when there is no actual File to format. ([#1525](https://github.com/diffplug/spotless/pull/1525))
1315

1416
## [2.33.0] - 2023-01-26
1517
### Added

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,4 @@ VER_DURIAN=1.2.0
2828
VER_JGIT=5.13.1.202206130422-r
2929
VER_JUNIT=5.9.2
3030
VER_ASSERTJ=3.24.2
31-
VER_MOCKITO=4.11.0
31+
VER_MOCKITO=4.11.0

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

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016 DiffPlug
2+
* Copyright 2016-2023 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.
@@ -237,8 +237,13 @@ public String compute(String unix, File file) {
237237
unix = LineEnding.toUnix(formatted);
238238
}
239239
} catch (Throwable e) {
240-
String relativePath = rootDir.relativize(file.toPath()).toString();
241-
exceptionPolicy.handleError(e, step, relativePath);
240+
if (file == NO_FILE_SENTINEL) {
241+
exceptionPolicy.handleError(e, step, "");
242+
} else {
243+
// Path may be forged from a different FileSystem than Filesystem.default
244+
String relativePath = rootDir.relativize(rootDir.getFileSystem().getPath(file.getPath())).toString();
245+
exceptionPolicy.handleError(e, step, relativePath);
246+
}
242247
}
243248
}
244249
return unix;
@@ -284,4 +289,7 @@ public void close() {
284289
}
285290
}
286291
}
292+
293+
/** This Sentinel reference may be used to pass string content to a Formatter or FormatterStep when there is no actual File to format */
294+
public static final File NO_FILE_SENTINEL = new File("NO_FILE_SENTINEL");
287295
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2021 DiffPlug
2+
* Copyright 2016-2023 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.
@@ -127,7 +127,7 @@ public String apply(String unix, File file) throws Exception {
127127

128128
@Override
129129
public String apply(String unix) throws Exception {
130-
return apply(unix, FormatterStepImpl.SENTINEL);
130+
return apply(unix, Formatter.NO_FILE_SENTINEL);
131131
}
132132
};
133133
}
@@ -156,7 +156,7 @@ default String apply(String unix, File file) throws Exception {
156156

157157
@Override
158158
default String apply(String unix) throws Exception {
159-
return apply(unix, FormatterStepImpl.SENTINEL);
159+
return apply(unix, Formatter.NO_FILE_SENTINEL);
160160
}
161161
}
162162
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2021 DiffPlug
2+
* Copyright 2016-2023 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.
@@ -37,8 +37,8 @@ public interface FormatterStep extends Serializable {
3737
* @param rawUnix
3838
* the content to format, guaranteed to have unix-style newlines ('\n'); never null
3939
* @param file
40-
* the file which {@code rawUnix} was obtained from; never null. Pass an empty file using
41-
* {@code new File("")} if and only if no file is actually associated with {@code rawUnix}
40+
* the file which {@code rawUnix} was obtained from; never null. Pass the reference
41+
* {@link Formatter#NO_FILE_SENTINEL} if and only if no file is actually associated with {@code rawUnix}
4242
* @return the formatted content, guaranteed to only have unix-style newlines; may return null
4343
* if the formatter step doesn't have any changes to make
4444
* @throws Exception if the formatter step experiences a problem

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

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2022 DiffPlug
2+
* Copyright 2016-2023 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.
@@ -116,11 +116,8 @@ protected String format(Integer state, String rawUnix, File file) throws Excepti
116116
}
117117
}
118118

119-
/** A dummy SENTINEL file. */
120-
static final File SENTINEL = new File("");
121-
122119
static void checkNotSentinel(File file) {
123-
if (file == SENTINEL) {
120+
if (file == Formatter.NO_FILE_SENTINEL) {
124121
throw new IllegalArgumentException("This step requires the underlying file. If this is a test, use StepHarnessWithFile");
125122
}
126123
}

testlib/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ dependencies {
1212
api "com.diffplug.durian:durian-testlib:${VER_DURIAN}"
1313
api "org.junit.jupiter:junit-jupiter:${VER_JUNIT}"
1414
api "org.assertj:assertj-core:${VER_ASSERTJ}"
15+
api "org.mockito:mockito-core:$VER_MOCKITO"
1516

1617
implementation "com.diffplug.durian:durian-io:${VER_DURIAN}"
1718
implementation "com.diffplug.durian:durian-collect:${VER_DURIAN}"

testlib/src/main/java/com/diffplug/spotless/StepHarness.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ public AbstractStringAssert<?> testResourceExceptionMsg(String resourceBefore) {
8888

8989
public AbstractStringAssert<?> testExceptionMsg(String before) {
9090
try {
91-
formatter.compute(LineEnding.toUnix(before), FormatterStepImpl.SENTINEL);
91+
formatter.compute(LineEnding.toUnix(before), Formatter.NO_FILE_SENTINEL);
9292
throw new SecurityException("Expected exception");
9393
} catch (Throwable e) {
9494
if (e instanceof SecurityException) {

testlib/src/test/java/com/diffplug/spotless/FormatterTest.java

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2022 DiffPlug
2+
* Copyright 2016-2023 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.
@@ -15,15 +15,20 @@
1515
*/
1616
package com.diffplug.spotless;
1717

18+
import java.io.File;
1819
import java.nio.charset.Charset;
1920
import java.nio.charset.StandardCharsets;
21+
import java.nio.file.FileSystem;
22+
import java.nio.file.FileSystems;
2023
import java.nio.file.Path;
2124
import java.nio.file.Paths;
2225
import java.util.ArrayList;
26+
import java.util.Collections;
2327
import java.util.List;
2428

2529
import org.junit.jupiter.api.Assertions;
2630
import org.junit.jupiter.api.Test;
31+
import org.mockito.Mockito;
2732

2833
import com.diffplug.common.base.StandardSystemProperty;
2934
import com.diffplug.spotless.generic.EndWithNewlineStep;
@@ -89,4 +94,95 @@ protected Formatter create() {
8994
}
9095
}.testEquals();
9196
}
97+
98+
// new File("") as filePath is known to fail
99+
@Test
100+
public void testExceptionWithEmptyPath() throws Exception {
101+
LineEnding.Policy lineEndingsPolicy = LineEnding.UNIX.createPolicy();
102+
Charset encoding = StandardCharsets.UTF_8;
103+
FormatExceptionPolicy exceptionPolicy = FormatExceptionPolicy.failOnlyOnError();
104+
105+
Path rootDir = Paths.get(StandardSystemProperty.USER_DIR.value());
106+
107+
FormatterStep step = Mockito.mock(FormatterStep.class);
108+
Mockito.when(step.getName()).thenReturn("someFailingStep");
109+
Mockito.when(step.format(Mockito.anyString(), Mockito.any(File.class))).thenThrow(new IllegalArgumentException("someReason"));
110+
List<FormatterStep> steps = Collections.singletonList(step);
111+
112+
Formatter formatter = Formatter.builder()
113+
.lineEndingsPolicy(lineEndingsPolicy)
114+
.encoding(encoding)
115+
.rootDir(rootDir)
116+
.steps(steps)
117+
.exceptionPolicy(exceptionPolicy)
118+
.build();
119+
120+
Assertions.assertThrows(IllegalArgumentException.class, () -> formatter.compute("someFileContent", new File("")));
121+
}
122+
123+
// If there is no File actually holding the content, one may rely on Formatter.NO_FILE_ON_DISK
124+
@Test
125+
public void testExceptionWithSentinelNoFileOnDisk() throws Exception {
126+
LineEnding.Policy lineEndingsPolicy = LineEnding.UNIX.createPolicy();
127+
Charset encoding = StandardCharsets.UTF_8;
128+
FormatExceptionPolicy exceptionPolicy = FormatExceptionPolicy.failOnlyOnError();
129+
130+
Path rootDir = Paths.get(StandardSystemProperty.USER_DIR.value());
131+
132+
FormatterStep step = Mockito.mock(FormatterStep.class);
133+
Mockito.when(step.getName()).thenReturn("someFailingStep");
134+
Mockito.when(step.format(Mockito.anyString(), Mockito.any(File.class))).thenThrow(new IllegalArgumentException("someReason"));
135+
List<FormatterStep> steps = Collections.singletonList(step);
136+
137+
Formatter formatter = Formatter.builder()
138+
.lineEndingsPolicy(lineEndingsPolicy)
139+
.encoding(encoding)
140+
.rootDir(rootDir)
141+
.steps(steps)
142+
.exceptionPolicy(exceptionPolicy)
143+
.build();
144+
145+
formatter.compute("someFileContent", Formatter.NO_FILE_SENTINEL);
146+
}
147+
148+
// rootDir may be a path not from the default FileSystem
149+
@Test
150+
public void testExceptionWithRootDirIsNotFileSystem() throws Exception {
151+
LineEnding.Policy lineEndingsPolicy = LineEnding.UNIX.createPolicy();
152+
Charset encoding = StandardCharsets.UTF_8;
153+
FormatExceptionPolicy exceptionPolicy = FormatExceptionPolicy.failOnlyOnError();
154+
155+
Path rootDir = Mockito.mock(Path.class);
156+
FileSystem customFileSystem = Mockito.mock(FileSystem.class);
157+
Mockito.when(rootDir.getFileSystem()).thenReturn(customFileSystem);
158+
159+
Path pathFromFile = Mockito.mock(Path.class);
160+
Mockito.when(customFileSystem.getPath(Mockito.anyString())).thenReturn(pathFromFile);
161+
162+
Path relativized = Mockito.mock(Path.class);
163+
Mockito.when(rootDir.relativize(Mockito.any(Path.class))).then(invok -> {
164+
Path filePath = invok.getArgument(0);
165+
if (filePath.getFileSystem() == FileSystems.getDefault()) {
166+
throw new IllegalArgumentException("Can not relativize through different FileSystems");
167+
}
168+
169+
return relativized;
170+
});
171+
172+
FormatterStep step = Mockito.mock(FormatterStep.class);
173+
Mockito.when(step.getName()).thenReturn("someFailingStep");
174+
Mockito.when(step.format(Mockito.anyString(), Mockito.any(File.class))).thenThrow(new IllegalArgumentException("someReason"));
175+
List<FormatterStep> steps = Collections.singletonList(step);
176+
177+
Formatter formatter = Formatter.builder()
178+
.lineEndingsPolicy(lineEndingsPolicy)
179+
.encoding(encoding)
180+
.rootDir(rootDir)
181+
.steps(steps)
182+
.exceptionPolicy(exceptionPolicy)
183+
.build();
184+
185+
formatter.compute("someFileContent", new File("/some/folder/some.file"));
186+
}
187+
92188
}

0 commit comments

Comments
 (0)