Skip to content

Commit

Permalink
Java importOrder support for sorting wildcards last (#954 fixes #879)
Browse files Browse the repository at this point in the history
  • Loading branch information
nedtwigg authored Oct 1, 2021
2 parents c687694 + 01383e0 commit 72e8c21
Show file tree
Hide file tree
Showing 12 changed files with 151 additions and 29 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ This document is intended for Spotless developers.
We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `1.27.0`).

## [Unreleased]
* Added `wildcardsLast` option for Java `ImportOrderStep` ([#954](https://github.com/diffplug/spotless/pull/954))

## [2.18.0] - 2021-09-30
### Added
Expand Down
26 changes: 19 additions & 7 deletions lib/src/main/java/com/diffplug/spotless/java/ImportOrderStep.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2020 DiffPlug
* Copyright 2016-2021 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -36,6 +36,8 @@
import com.diffplug.spotless.FormatterStep;

public final class ImportOrderStep {
private static final boolean WILDCARDS_LAST_DEFAULT = false;

private final String lineFormat;

public static ImportOrderStep forGroovy() {
Expand All @@ -51,19 +53,27 @@ private ImportOrderStep(String lineFormat) {
}

public FormatterStep createFrom(String... importOrder) {
return createFrom(WILDCARDS_LAST_DEFAULT, importOrder);
}

public FormatterStep createFrom(boolean wildcardsLast, String... importOrder) {
// defensive copying and null checking
List<String> importOrderList = requireElementsNonNull(Arrays.asList(importOrder));
return createFrom(() -> importOrderList);
return createFrom(wildcardsLast, () -> importOrderList);
}

public FormatterStep createFrom(File importsFile) {
return createFrom(WILDCARDS_LAST_DEFAULT, importsFile);
}

public FormatterStep createFrom(boolean wildcardsLast, File importsFile) {
Objects.requireNonNull(importsFile);
return createFrom(() -> getImportOrder(importsFile));
return createFrom(wildcardsLast, () -> getImportOrder(importsFile));
}

private FormatterStep createFrom(Supplier<List<String>> importOrder) {
private FormatterStep createFrom(boolean wildcardsLast, Supplier<List<String>> importOrder) {
return FormatterStep.createLazy("importOrder",
() -> new State(importOrder.get(), lineFormat),
() -> new State(importOrder.get(), lineFormat, wildcardsLast),
State::toFormatter);
}

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

private final List<String> importOrder;
private final String lineFormat;
private final boolean wildcardsLast;

State(List<String> importOrder, String lineFormat) {
State(List<String> importOrder, String lineFormat, boolean wildcardsLast) {
this.importOrder = importOrder;
this.lineFormat = lineFormat;
this.wildcardsLast = wildcardsLast;
}

FormatterFunc toFormatter() {
return raw -> new ImportSorter(importOrder).format(raw, lineFormat);
return raw -> new ImportSorter(importOrder, wildcardsLast).format(raw, lineFormat);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016 DiffPlug
* Copyright 2016-2021 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -29,9 +29,11 @@ final class ImportSorter {
static final String N = "\n";

private final List<String> importsOrder;
private final boolean wildcardsLast;

ImportSorter(List<String> importsOrder) {
ImportSorter(List<String> importsOrder, boolean wildcardsLast) {
this.importsOrder = new ArrayList<>(importsOrder);
this.wildcardsLast = wildcardsLast;
}

String format(String raw, String lineFormat) {
Expand Down Expand Up @@ -79,7 +81,7 @@ String format(String raw, String lineFormat) {
}
scanner.close();

List<String> sortedImports = ImportSorterImpl.sort(imports, importsOrder, lineFormat);
List<String> sortedImports = ImportSorterImpl.sort(imports, importsOrder, wildcardsLast, lineFormat);
return applyImportsToDocument(raw, firstImportLine, lastImportLine, sortedImports);
}

Expand Down
50 changes: 37 additions & 13 deletions lib/src/main/java/com/diffplug/spotless/java/ImportSorterImpl.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016 DiffPlug
* Copyright 2016-2021 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -15,13 +15,7 @@
*/
package com.diffplug.spotless.java;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;

import javax.annotation.Nullable;

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

static List<String> sort(List<String> imports, List<String> importsOrder, String lineFormat) {
ImportSorterImpl importsSorter = new ImportSorterImpl(importsOrder);
static List<String> sort(List<String> imports, List<String> importsOrder, boolean wildcardsLast, String lineFormat) {
ImportSorterImpl importsSorter = new ImportSorterImpl(importsOrder, wildcardsLast);
return importsSorter.sort(imports, lineFormat);
}

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

private ImportSorterImpl(List<String> importOrder) {
private ImportSorterImpl(List<String> importOrder, boolean wildcardsLast) {
List<String> importOrderCopy = new ArrayList<>(importOrder);
normalizeStaticOrderItems(importOrderCopy);
putStaticItemIfNotExists(importOrderCopy);
template.addAll(importOrderCopy);
ordering = new OrderingComparator(wildcardsLast);
this.allImportOrderItems.addAll(importOrderCopy);
}

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

int firstIndexOfOrderItem = getFirstIndexOfOrderItem(notMatching, staticItems);
int indexOfOrderItem = 0;
Expand Down Expand Up @@ -192,7 +188,7 @@ private void mergeMatchingItems() {
continue;
}
List<String> matchingItems = new ArrayList<>(strings);
Collections.sort(matchingItems);
sort(matchingItems);

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

private void sort(List<String> items) {
items.sort(ordering);
}

private List<String> getResult(String lineFormat) {
List<String> strings = new ArrayList<>();

Expand Down Expand Up @@ -256,4 +256,28 @@ private List<String> getResult(String lineFormat) {
return null;
}

private static class OrderingComparator implements Comparator<String> {
private final boolean wildcardsLast;

private OrderingComparator(boolean wildcardsLast) {
this.wildcardsLast = wildcardsLast;
}

@Override
public int compare(String string1, String string2) {
int string1WildcardIndex = string1.indexOf('*');
int string2WildcardIndex = string2.indexOf('*');
boolean string1IsWildcard = string1WildcardIndex >= 0;
boolean string2IsWildcard = string2WildcardIndex >= 0;
if (string1IsWildcard == string2IsWildcard) {
return string1.compareTo(string2);
}
int prefixLength = string1IsWildcard ? string1WildcardIndex : string2WildcardIndex;
boolean samePrefix = string1.regionMatches(0, string2, 0, prefixLength);
if (!samePrefix) {
return string1.compareTo(string2);
}
return (string1IsWildcard == wildcardsLast) ? 1 : -1;
}
}
}
1 change: 1 addition & 0 deletions plugin-gradle/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `3.27.0`).

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

## [5.15.2] - 2021-09-27
### Changed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

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

import java.io.File;
import java.util.Objects;

import javax.inject.Inject;
Expand Down Expand Up @@ -57,13 +58,51 @@ public LicenseHeaderConfig licenseHeaderFile(Object licenseHeaderFile) {
return licenseHeaderFile(licenseHeaderFile, LICENSE_HEADER_DELIMITER);
}

public void importOrder(String... importOrder) {
addStep(ImportOrderStep.forJava().createFrom(importOrder));
public ImportOrderConfig importOrder(String... importOrder) {
return new ImportOrderConfig(importOrder);
}

public void importOrderFile(Object importOrderFile) {
public ImportOrderConfig importOrderFile(Object importOrderFile) {
Objects.requireNonNull(importOrderFile);
addStep(ImportOrderStep.forJava().createFrom(getProject().file(importOrderFile)));
return new ImportOrderConfig(getProject().file(importOrderFile));
}

public class ImportOrderConfig {
final String[] importOrder;
final File importOrderFile;

boolean wildcardsLast = false;

ImportOrderConfig(String[] importOrder) {
this.importOrder = importOrder;
importOrderFile = null;
addStep(createStep());
}

ImportOrderConfig(File importOrderFile) {
importOrder = null;
this.importOrderFile = importOrderFile;
addStep(createStep());
}

/** Sorts wildcard imports after non-wildcard imports, instead of before. */
public ImportOrderConfig wildcardsLast() {
return wildcardsLast(true);
}

public ImportOrderConfig wildcardsLast(boolean wildcardsLast) {
this.wildcardsLast = wildcardsLast;
replaceStep(createStep());
return this;
}

private FormatterStep createStep() {
ImportOrderStep importOrderStep = ImportOrderStep.forJava();

return importOrderFile != null
? importOrderStep.createFrom(wildcardsLast, getProject().file(importOrderFile))
: importOrderStep.createFrom(wildcardsLast, importOrder);
}
}

/** Removes any unused imports. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,14 @@ import java.lang.Runnable;
import org.comments.be
import org.comments.removed
*/
import static java.lang.Runnable.*;
import static java.lang.Runnable.*;
/*
import other.multiline.comments
import will.be.removed.too */
import will.be.removed.too */
import static com.foo.Bar
import java.awt.*;
import java.util.*;
import java.util.List;
import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
import static org.hamcrest.Matchers.*;
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import java.awt.*;
import java.lang.Runnable;
import java.lang.Thread;
import java.util.*;
import java.util.List;

import org.dooda.Didoo;

import static java.lang.Exception.*;
import static java.lang.Runnable.*;
import static org.hamcrest.Matchers.*;

import static com.foo.Bar;
import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import static com.foo.Bar;
import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
import static java.lang.Exception.*;
import static java.lang.Runnable.*;
import static org.hamcrest.Matchers.*;

import java.awt.*;
import java.lang.Runnable;
import java.lang.Thread;
import java.util.*;
import java.util.List;
import org.dooda.Didoo;
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import static com.foo.Bar;
import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static java.lang.Exception.*;
import static java.lang.Runnable.*;
import static org.hamcrest.Matchers.*;

import java.awt.*;
import java.lang.Runnable;
import java.lang.Thread;
import java.util.List;
import java.util.*;
import org.dooda.Didoo;
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import static java.lang.Exception.*;
import static com.github.tomakehurst.wiremock.client.WireMock.*;
import org.dooda.Didoo;
import java.util.List;
import java.lang.Thread;
import java.util.*;
import java.lang.Runnable;
import static org.hamcrest.Matchers.*;

import static java.lang.Runnable.*;
import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
import static com.foo.Bar
import java.awt.*;
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ void sortImportsUnmatched() throws Throwable {
assertOnResources(step, "java/importsorter/JavaCodeUnsortedImportsUnmatched.test", "java/importsorter/JavaCodeSortedImportsUnmatched.test");
}

@Test
void sortImportsWildcardsLast() throws Throwable {
FormatterStep step = ImportOrderStep.forJava().createFrom(true);
assertOnResources(step, "java/importsorter/JavaCodeUnsortedImports.test", "java/importsorter/JavaCodeSortedImportsWildcardsLast.test");
}

@Test
void removeDuplicates() throws Throwable {
FormatterStep step = ImportOrderStep.forJava().createFrom(createTestFile("java/importsorter/import_unmatched.properties"));
Expand Down

0 comments on commit 72e8c21

Please sign in to comment.