net.revelc.code.impsort.Grouper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of impsort-maven-plugin Show documentation
Show all versions of impsort-maven-plugin Show documentation
Sort your imps! A Maven plugin to sort the import statements in your Java source files.
The newest version!
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.revelc.code.impsort;
import static java.util.Objects.requireNonNull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.regex.Pattern;
public final class Grouper {
private final List groups;
private final List staticGroups;
private final boolean staticAfter;
private final boolean joinStaticWithNonStatic;
private final boolean breadthFirstStatic;
public Grouper(String groups, String staticGroups, boolean staticAfter,
boolean joinStaticWithNonStatic, boolean breadthFirstStatic) {
this.groups = Collections.unmodifiableList(parse(groups));
this.staticGroups = parse(staticGroups);
this.staticAfter = staticAfter;
this.joinStaticWithNonStatic = joinStaticWithNonStatic;
this.breadthFirstStatic = breadthFirstStatic;
}
static ArrayList parse(String groups) {
ArrayList parsedGroups = new ArrayList<>();
String[] array = requireNonNull(groups).replaceAll("\\s+", "").split(",");
Pattern validGroup = Pattern.compile("^(?:\\w+(?:[.]\\w+)*[.]?|[*])$");
// skip special case where the first element from split is empty and is the only element
if (array.length != 1 || !array[0].isEmpty()) {
for (String g : array) {
if (!validGroup.matcher(g).matches()) {
throw new IllegalArgumentException("Invalid group (" + g + ") in (" + groups + ")");
}
if (parsedGroups.stream().anyMatch(o -> g.contentEquals(o.getPrefix()))) {
throw new IllegalArgumentException("Duplicate group (" + g + ") in (" + groups + ")");
}
int encounterOrder = parsedGroups.size();
parsedGroups.add(new Group(g, encounterOrder));
}
}
// include the default group if not already included
if (parsedGroups.stream().noneMatch(o -> "*".contentEquals(o.getPrefix()))) {
parsedGroups.add(new Group("*", parsedGroups.size()));
}
parsedGroups.sort((a, b) -> {
// sort in reverse prefix length order first, then encounter order
int comp = Integer.compare(b.getPrefix().length(), a.getPrefix().length());
return comp != 0 ? comp : Integer.compare(a.getOrder(), a.getOrder());
});
return parsedGroups;
}
static Comparator depthFirstComparator = (a, b) -> a.getImport().compareTo(b.getImport());
static Comparator breadthFirstComparator = (a, b) -> {
String first = a.getImport();
String second = b.getImport();
int firstLastDot = first.lastIndexOf(".");
int secondLastDot = second.lastIndexOf(".");
String firstContainingClass = first.substring(0, firstLastDot);
String secondContainingClass = second.substring(0, secondLastDot);
int comparison = firstContainingClass.compareTo(secondContainingClass);
if (comparison == 0) {
comparison = first.substring(firstLastDot).compareTo(second.substring(secondLastDot));
}
return comparison;
};
public Map> groupNonStatic(Collection allImports) {
return group(allImports, groups, i -> !i.isStatic(), depthFirstComparator);
}
public Map> groupStatic(Collection allImports) {
return group(allImports, staticGroups, i -> i.isStatic(),
breadthFirstStatic ? breadthFirstComparator : depthFirstComparator);
}
private static Map> group(Collection allImports,
List groups, Predicate filter, Comparator itemComparator) {
Map> map = new TreeMap<>();
allImports.stream().filter(filter).forEach(imp -> {
for (Group group : groups) {
if (group.matches(imp.getImport())) {
map.computeIfAbsent(group.getOrder(), x -> new ArrayList<>()).add(imp);
break;
}
}
});
for (ArrayList list : map.values()) {
list.sort(itemComparator);
}
return map;
}
public boolean getStaticAfter() {
return staticAfter;
}
public boolean getJoinStaticWithNonStatic() {
return joinStaticWithNonStatic;
}
public String groupedImports(Collection allImports, String eol) {
StringBuilder sb = new StringBuilder();
Map> staticImports = groupStatic(allImports);
Map> nonStaticImports = groupNonStatic(allImports);
Map> first = getStaticAfter() ? nonStaticImports : staticImports;
Map> second = getStaticAfter() ? staticImports : nonStaticImports;
AtomicBoolean firstGroup = new AtomicBoolean(true);
Consumer> consumer = grouping -> {
if (!firstGroup.getAndSet(false)) {
sb.append(eol);
}
grouping.forEach(imp -> sb.append(imp).append(eol));
};
first.values().forEach(consumer);
if (!getJoinStaticWithNonStatic() && !first.isEmpty() && !second.isEmpty()) {
sb.append(eol);
}
firstGroup.set(true);
second.values().forEach(consumer);
// allImports.forEach(x -> System.out.print("-----\n" + x + "\n-----"));
return sb.toString();
}
}