Skip to content

Commit 3b74071

Browse files
committed
Java importOrder support for sorting wildcards last
Adds support for sorting wildcards after non-wildcards in the import list. Gradle config like so: ``` importOrder('java', 'javax', 'com.acme', '').wildcardsLast() ``` Not implemented here: Maven plugin support Fixes diffplug#879.
1 parent ae5529e commit 3b74071

File tree

10 files changed

+141
-27
lines changed

10 files changed

+141
-27
lines changed

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

+17-7
Original file line numberDiff line numberDiff line change
@@ -51,20 +51,28 @@ private ImportOrderStep(String lineFormat) {
5151
}
5252

5353
public FormatterStep createFrom(String... importOrder) {
54+
return createFrom(false, importOrder);
55+
}
56+
57+
public FormatterStep createFrom(boolean wildcardsLast, String... importOrder) {
5458
// defensive copying and null checking
5559
List<String> importOrderList = requireElementsNonNull(Arrays.asList(importOrder));
56-
return createFrom(() -> importOrderList);
60+
return createFrom(wildcardsLast, () -> importOrderList);
5761
}
5862

5963
public FormatterStep createFrom(File importsFile) {
64+
return createFrom(false, importsFile);
65+
}
66+
67+
public FormatterStep createFrom(boolean wildcardsLast, File importsFile) {
6068
Objects.requireNonNull(importsFile);
61-
return createFrom(() -> getImportOrder(importsFile));
69+
return createFrom(wildcardsLast, () -> getImportOrder(importsFile));
6270
}
6371

64-
private FormatterStep createFrom(Supplier<List<String>> importOrder) {
72+
private FormatterStep createFrom(boolean wildcardsLast, Supplier<List<String>> importOrder) {
6573
return FormatterStep.createLazy("importOrder",
66-
() -> new State(importOrder.get(), lineFormat),
67-
State::toFormatter);
74+
() -> new State(importOrder.get(), lineFormat, wildcardsLast),
75+
State::toFormatter);
6876
}
6977

7078
private static List<String> getImportOrder(File importsFile) {
@@ -92,14 +100,16 @@ private static final class State implements Serializable {
92100

93101
private final List<String> importOrder;
94102
private final String lineFormat;
103+
private final boolean wildcardsLast;
95104

96-
State(List<String> importOrder, String lineFormat) {
105+
State(List<String> importOrder, String lineFormat, boolean wildcardsLast) {
97106
this.importOrder = importOrder;
98107
this.lineFormat = lineFormat;
108+
this.wildcardsLast = wildcardsLast;
99109
}
100110

101111
FormatterFunc toFormatter() {
102-
return raw -> new ImportSorter(importOrder).format(raw, lineFormat);
112+
return raw -> new ImportSorter(importOrder, wildcardsLast).format(raw, lineFormat);
103113
}
104114
}
105115
}

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

+4-2
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,11 @@ final class ImportSorter {
2929
static final String N = "\n";
3030

3131
private final List<String> importsOrder;
32+
private final boolean wildcardsLast;
3233

33-
ImportSorter(List<String> importsOrder) {
34+
ImportSorter(List<String> importsOrder, boolean wildcardsLast) {
3435
this.importsOrder = new ArrayList<>(importsOrder);
36+
this.wildcardsLast = wildcardsLast;
3537
}
3638

3739
String format(String raw, String lineFormat) {
@@ -79,7 +81,7 @@ String format(String raw, String lineFormat) {
7981
}
8082
scanner.close();
8183

82-
List<String> sortedImports = ImportSorterImpl.sort(imports, importsOrder, lineFormat);
84+
List<String> sortedImports = ImportSorterImpl.sort(imports, importsOrder, wildcardsLast, lineFormat);
8385
return applyImportsToDocument(raw, firstImportLine, lastImportLine, sortedImports);
8486
}
8587

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

+36-12
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,7 @@
1515
*/
1616
package com.diffplug.spotless.java;
1717

18-
import java.util.ArrayList;
19-
import java.util.Collections;
20-
import java.util.HashMap;
21-
import java.util.HashSet;
22-
import java.util.List;
23-
import java.util.Map;
24-
import java.util.Set;
18+
import java.util.*;
2519

2620
import javax.annotation.Nullable;
2721

@@ -34,9 +28,10 @@ final class ImportSorterImpl {
3428
private final Map<String, List<String>> matchingImports = new HashMap<>();
3529
private final List<String> notMatching = new ArrayList<>();
3630
private final Set<String> allImportOrderItems = new HashSet<>();
31+
private final Comparator<String> ordering;
3732

38-
static List<String> sort(List<String> imports, List<String> importsOrder, String lineFormat) {
39-
ImportSorterImpl importsSorter = new ImportSorterImpl(importsOrder);
33+
static List<String> sort(List<String> imports, List<String> importsOrder, boolean wildcardsLast, String lineFormat) {
34+
ImportSorterImpl importsSorter = new ImportSorterImpl(importsOrder, wildcardsLast);
4035
return importsSorter.sort(imports, lineFormat);
4136
}
4237

@@ -49,11 +44,12 @@ private List<String> sort(List<String> imports, String lineFormat) {
4944
return getResult(lineFormat);
5045
}
5146

52-
private ImportSorterImpl(List<String> importOrder) {
47+
private ImportSorterImpl(List<String> importOrder, boolean wildcardsLast) {
5348
List<String> importOrderCopy = new ArrayList<>(importOrder);
5449
normalizeStaticOrderItems(importOrderCopy);
5550
putStaticItemIfNotExists(importOrderCopy);
5651
template.addAll(importOrderCopy);
52+
ordering = new OrderingComparator(wildcardsLast);
5753
this.allImportOrderItems.addAll(importOrderCopy);
5854
}
5955

@@ -119,7 +115,7 @@ private void filterMatchingImports(List<String> imports) {
119115
* not matching means it does not match any order item, so it will be appended before or after order items
120116
*/
121117
private void mergeNotMatchingItems(boolean staticItems) {
122-
Collections.sort(notMatching);
118+
sort(notMatching);
123119

124120
int firstIndexOfOrderItem = getFirstIndexOfOrderItem(notMatching, staticItems);
125121
int indexOfOrderItem = 0;
@@ -192,7 +188,7 @@ private void mergeMatchingItems() {
192188
continue;
193189
}
194190
List<String> matchingItems = new ArrayList<>(strings);
195-
Collections.sort(matchingItems);
191+
sort(matchingItems);
196192

197193
// replace order item by matching import statements
198194
// this is a mess and it is only a luck that it works :-]
@@ -218,6 +214,10 @@ private void mergeMatchingItems() {
218214
}
219215
}
220216

217+
private void sort(List<String> items) {
218+
items.sort(ordering);
219+
}
220+
221221
private List<String> getResult(String lineFormat) {
222222
List<String> strings = new ArrayList<>();
223223

@@ -256,4 +256,28 @@ private List<String> getResult(String lineFormat) {
256256
return null;
257257
}
258258

259+
private static class OrderingComparator implements Comparator<String> {
260+
private final boolean wildcardsLast;
261+
262+
private OrderingComparator(boolean wildcardsLast) {
263+
this.wildcardsLast = wildcardsLast;
264+
}
265+
266+
@Override
267+
public int compare(String string1, String string2) {
268+
int string1WildcardIndex = string1.indexOf('*');
269+
int string2WildcardIndex = string2.indexOf('*');
270+
boolean string1IsWildcard = string1WildcardIndex >= 0;
271+
boolean string2IsWildcard = string2WildcardIndex >= 0;
272+
if (string1IsWildcard == string2IsWildcard) {
273+
return string1.compareTo(string2);
274+
}
275+
int prefixLength = string1IsWildcard ? string1WildcardIndex : string2WildcardIndex;
276+
boolean samePrefix = string1.regionMatches(0, string2, 0, prefixLength);
277+
if (!samePrefix) {
278+
return string1.compareTo(string2);
279+
}
280+
return (string1IsWildcard == wildcardsLast) ? 1 : -1;
281+
}
282+
}
259283
}

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

+39-4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import static com.diffplug.gradle.spotless.PluginGradlePreconditions.requireElementsNonNull;
1919

20+
import java.io.File;
2021
import java.util.Objects;
2122

2223
import javax.inject.Inject;
@@ -57,13 +58,47 @@ public LicenseHeaderConfig licenseHeaderFile(Object licenseHeaderFile) {
5758
return licenseHeaderFile(licenseHeaderFile, LICENSE_HEADER_DELIMITER);
5859
}
5960

60-
public void importOrder(String... importOrder) {
61-
addStep(ImportOrderStep.forJava().createFrom(importOrder));
61+
public ImportOrderConfig importOrder(String... importOrder) {
62+
return new ImportOrderConfig(importOrder);
6263
}
6364

64-
public void importOrderFile(Object importOrderFile) {
65+
public ImportOrderConfig importOrderFile(Object importOrderFile) {
6566
Objects.requireNonNull(importOrderFile);
66-
addStep(ImportOrderStep.forJava().createFrom(getProject().file(importOrderFile)));
67+
return new ImportOrderConfig(getProject().file(importOrderFile));
68+
}
69+
70+
public class ImportOrderConfig {
71+
final String[] importOrder;
72+
final File importOrderFile;
73+
74+
boolean wildcardsLast = false;
75+
76+
ImportOrderConfig(String[] importOrder) {
77+
this.importOrder = importOrder;
78+
importOrderFile = null;
79+
addStep(createStep());
80+
}
81+
82+
ImportOrderConfig(File importOrderFile) {
83+
importOrder = null;
84+
this.importOrderFile = importOrderFile;
85+
addStep(createStep());
86+
}
87+
88+
/** Sorts wildcard imports after non-wildcard imports, instead of before. */
89+
public ImportOrderConfig wildcardsLast() {
90+
wildcardsLast = true;
91+
replaceStep(createStep());
92+
return this;
93+
}
94+
95+
private FormatterStep createStep() {
96+
ImportOrderStep importOrderStep = ImportOrderStep.forJava();
97+
98+
return importOrderFile != null
99+
? importOrderStep.createFrom(wildcardsLast, getProject().file(importOrderFile))
100+
: importOrderStep.createFrom(wildcardsLast, importOrder);
101+
}
67102
}
68103

69104
/** Removes any unused imports. */

testlib/src/main/resources/java/importsorter/JavaCodeImportComments.test

+8-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,14 @@ import java.lang.Runnable;
77
import org.comments.be
88
import org.comments.removed
99
*/
10-
import static java.lang.Runnable.*;
10+
import static java.lang.Runnable.*;
1111
/*
1212
import other.multiline.comments
13-
import will.be.removed.too */
13+
import will.be.removed.too */
1414
import static com.foo.Bar
15+
import java.awt.*;
16+
import java.util.*;
17+
import java.util.List;
18+
import static com.github.tomakehurst.wiremock.client.WireMock.*;
19+
import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
20+
import static org.hamcrest.Matchers.*;
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
1+
import java.awt.*;
12
import java.lang.Runnable;
23
import java.lang.Thread;
4+
import java.util.*;
5+
import java.util.List;
36

47
import org.dooda.Didoo;
58

69
import static java.lang.Exception.*;
710
import static java.lang.Runnable.*;
11+
import static org.hamcrest.Matchers.*;
812

913
import static com.foo.Bar;
14+
import static com.github.tomakehurst.wiremock.client.WireMock.*;
15+
import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
import static com.foo.Bar;
2+
import static com.github.tomakehurst.wiremock.client.WireMock.*;
3+
import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
24
import static java.lang.Exception.*;
35
import static java.lang.Runnable.*;
6+
import static org.hamcrest.Matchers.*;
47

8+
import java.awt.*;
59
import java.lang.Runnable;
610
import java.lang.Thread;
11+
import java.util.*;
12+
import java.util.List;
713
import org.dooda.Didoo;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import static com.foo.Bar;
2+
import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
3+
import static com.github.tomakehurst.wiremock.client.WireMock.*;
4+
import static java.lang.Exception.*;
5+
import static java.lang.Runnable.*;
6+
import static org.hamcrest.Matchers.*;
7+
8+
import java.awt.*;
9+
import java.lang.Runnable;
10+
import java.lang.Thread;
11+
import java.util.List;
12+
import java.util.*;
13+
import org.dooda.Didoo;
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
import static java.lang.Exception.*;
2+
import static com.github.tomakehurst.wiremock.client.WireMock.*;
23
import org.dooda.Didoo;
4+
import java.util.List;
35
import java.lang.Thread;
6+
import java.util.*;
47
import java.lang.Runnable;
8+
import static org.hamcrest.Matchers.*;
59

610
import static java.lang.Runnable.*;
11+
import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
712
import static com.foo.Bar
13+
import java.awt.*;

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

+6
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ void sortImportsUnmatched() throws Throwable {
4646
assertOnResources(step, "java/importsorter/JavaCodeUnsortedImportsUnmatched.test", "java/importsorter/JavaCodeSortedImportsUnmatched.test");
4747
}
4848

49+
@Test
50+
void sortImportsWildcardsLast() throws Throwable {
51+
FormatterStep step = ImportOrderStep.forJava().createFrom(true);
52+
assertOnResources(step, "java/importsorter/JavaCodeUnsortedImports.test", "java/importsorter/JavaCodeSortedImportsWildcardsLast.test");
53+
}
54+
4955
@Test
5056
void removeDuplicates() throws Throwable {
5157
FormatterStep step = ImportOrderStep.forJava().createFrom(createTestFile("java/importsorter/import_unmatched.properties"));

0 commit comments

Comments
 (0)