Skip to content

Commit 72e8c21

Browse files
authored
Java importOrder support for sorting wildcards last (#954 fixes #879)
2 parents c687694 + 01383e0 commit 72e8c21

File tree

12 files changed

+151
-29
lines changed

12 files changed

+151
-29
lines changed

CHANGES.md

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ 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 `wildcardsLast` option for Java `ImportOrderStep` ([#954](https://github.com/diffplug/spotless/pull/954))
1314

1415
## [2.18.0] - 2021-09-30
1516
### Added

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

+19-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2020 DiffPlug
2+
* Copyright 2016-2021 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,6 +36,8 @@
3636
import com.diffplug.spotless.FormatterStep;
3737

3838
public final class ImportOrderStep {
39+
private static final boolean WILDCARDS_LAST_DEFAULT = false;
40+
3941
private final String lineFormat;
4042

4143
public static ImportOrderStep forGroovy() {
@@ -51,19 +53,27 @@ private ImportOrderStep(String lineFormat) {
5153
}
5254

5355
public FormatterStep createFrom(String... importOrder) {
56+
return createFrom(WILDCARDS_LAST_DEFAULT, importOrder);
57+
}
58+
59+
public FormatterStep createFrom(boolean wildcardsLast, String... importOrder) {
5460
// defensive copying and null checking
5561
List<String> importOrderList = requireElementsNonNull(Arrays.asList(importOrder));
56-
return createFrom(() -> importOrderList);
62+
return createFrom(wildcardsLast, () -> importOrderList);
5763
}
5864

5965
public FormatterStep createFrom(File importsFile) {
66+
return createFrom(WILDCARDS_LAST_DEFAULT, importsFile);
67+
}
68+
69+
public FormatterStep createFrom(boolean wildcardsLast, File importsFile) {
6070
Objects.requireNonNull(importsFile);
61-
return createFrom(() -> getImportOrder(importsFile));
71+
return createFrom(wildcardsLast, () -> getImportOrder(importsFile));
6272
}
6373

64-
private FormatterStep createFrom(Supplier<List<String>> importOrder) {
74+
private FormatterStep createFrom(boolean wildcardsLast, Supplier<List<String>> importOrder) {
6575
return FormatterStep.createLazy("importOrder",
66-
() -> new State(importOrder.get(), lineFormat),
76+
() -> new State(importOrder.get(), lineFormat, wildcardsLast),
6777
State::toFormatter);
6878
}
6979

@@ -92,14 +102,16 @@ private static final class State implements Serializable {
92102

93103
private final List<String> importOrder;
94104
private final String lineFormat;
105+
private final boolean wildcardsLast;
95106

96-
State(List<String> importOrder, String lineFormat) {
107+
State(List<String> importOrder, String lineFormat, boolean wildcardsLast) {
97108
this.importOrder = importOrder;
98109
this.lineFormat = lineFormat;
110+
this.wildcardsLast = wildcardsLast;
99111
}
100112

101113
FormatterFunc toFormatter() {
102-
return raw -> new ImportSorter(importOrder).format(raw, lineFormat);
114+
return raw -> new ImportSorter(importOrder, wildcardsLast).format(raw, lineFormat);
103115
}
104116
}
105117
}

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

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016 DiffPlug
2+
* Copyright 2016-2021 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.
@@ -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

+37-13
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016 DiffPlug
2+
* Copyright 2016-2021 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,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/CHANGES.md

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `3.27.0`).
44

55
## [Unreleased]
6+
* Added `wildcardsLast()` option for Java `importOrder` ([#954](https://github.com/diffplug/spotless/pull/954))
67

78
## [5.15.2] - 2021-09-27
89
### Changed

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

+43-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,51 @@ 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+
return wildcardsLast(true);
91+
}
92+
93+
public ImportOrderConfig wildcardsLast(boolean wildcardsLast) {
94+
this.wildcardsLast = wildcardsLast;
95+
replaceStep(createStep());
96+
return this;
97+
}
98+
99+
private FormatterStep createStep() {
100+
ImportOrderStep importOrderStep = ImportOrderStep.forJava();
101+
102+
return importOrderFile != null
103+
? importOrderStep.createFrom(wildcardsLast, getProject().file(importOrderFile))
104+
: importOrderStep.createFrom(wildcardsLast, importOrder);
105+
}
67106
}
68107

69108
/** 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)