All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openrewrite.java.OrderImports Maven / Gradle / Ivy

/*
 * Copyright 2020 the original author or authors.
 * 

* 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 org.openrewrite.java; import com.fasterxml.jackson.annotation.JsonIgnore; import org.openrewrite.Formatting; import org.openrewrite.Validated; import org.openrewrite.java.tree.J; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.List; import java.util.regex.Pattern; import static java.util.Collections.emptyList; import static java.util.stream.Collectors.toList; import static org.openrewrite.Tree.randomId; import static org.openrewrite.Validated.valid; public class OrderImports extends JavaRefactorVisitor { public static OrderImports DEFAULT = new OrderImports(); static { DEFAULT.setLayout(intellij()); } // VisibleForTesting final static Comparator IMPORT_SORTING = (i1, i2) -> { String[] import1 = i1.getQualid().printTrimmed().split("\\."); String[] import2 = i2.getQualid().printTrimmed().split("\\."); for (int i = 0; i < Math.min(import1.length, import2.length); i++) { int diff = import1[i].compareTo(import2[i]); if (diff != 0) { return diff; } } if (import1.length == import2.length) { return 0; } return import1.length > import2.length ? 1 : -1; }; // VisibleForTesting Layout importLayout = intellij(); private boolean removeUnused = true; @JsonIgnore public void setLayout(Layout importLayout) { this.importLayout = importLayout; } public void setRemoveUnused(boolean removeUnused) { this.removeUnused = removeUnused; } /** * @return The default import ordering of IntelliJ IDEA. */ public static OrderImports.Layout intellij() { return Layout.builder(5, 3) .importAllOthers() .blankLine() .importPackage("javax.*") .importPackage("java.*") .blankLine() .importStaticAllOthers() .build(); } @Override public Validated validate() { return importLayout.validate(); } @Override public J visitCompilationUnit(J.CompilationUnit cu) { List orderedImports = new ArrayList<>(); int importIndex = 0; String extraLineSpace = ""; List blocks = importLayout.blocks; for (Layout.Block block : blocks) { if (block instanceof Layout.Block.BlankLines) { extraLineSpace = ""; for (int i = 0; i < ((Layout.Block.BlankLines) block).count; i++) { //noinspection StringConcatenationInLoop extraLineSpace += "\n"; } } else { block.reset(); cu.getImports().forEach(block::accept); for (J.Import orderedImport : block.orderedImports()) { String prefix = importIndex == 0 ? cu.getImports().get(0).getPrefix() : extraLineSpace + "\n"; if (!orderedImport.getPrefix().equals(prefix)) { orderedImports.add(orderedImport.withPrefix(prefix)); } else { orderedImports.add(orderedImport); } extraLineSpace = ""; importIndex++; } } } if (removeUnused) { andThen(new RemoveUnusedImports( importLayout.classCountToUseStarImport, importLayout.nameCountToUseStarImport)); } if (orderedImports.size() != cu.getImports().size()) { return cu.withImports(orderedImports); } for (int i = 0; i < orderedImports.size(); i++) { if (orderedImports.get(i) != cu.getImports().get(i)) { return cu.withImports(orderedImports); } } return cu; } public static class Layout { private final List blocks; private final int classCountToUseStarImport; private final int nameCountToUseStarImport; Layout(List blocks, int classCountToUseStarImport, int nameCountToUseStarImport) { this.blocks = blocks; this.classCountToUseStarImport = classCountToUseStarImport; this.nameCountToUseStarImport = nameCountToUseStarImport; } public List getBlocks() { return blocks; } public int getClassCountToUseStarImport() { return classCountToUseStarImport; } public int getNameCountToUseStarImport() { return nameCountToUseStarImport; } public interface Block { boolean accept(J.Import anImport); void reset(); /** * @return Imports belonging to this block, folded appropriately. */ List orderedImports(); class BlankLines implements Block { private int count = 1; @Override public boolean accept(J.Import anImport) { return false; } @Override public void reset() { } @Override public List orderedImports() { return emptyList(); } } class ImportPackage implements Block { private final List imports = new ArrayList<>(); private final boolean statik; private final Pattern packageWildcard; private final int classCountToUseStarImport; private final int nameCountToUseStarImport; public ImportPackage(boolean statik, String packageWildcard, boolean withSubpackages, int classCountToUseStarImport, int nameCountToUseStarImport) { this.statik = statik; this.classCountToUseStarImport = classCountToUseStarImport; this.nameCountToUseStarImport = nameCountToUseStarImport; this.packageWildcard = Pattern.compile(packageWildcard .replace(".", "\\.") .replace("*", withSubpackages ? ".+" : "[^.]+")); } @Override public boolean accept(J.Import anImport) { if (anImport.isStatic() == statik && packageWildcard.matcher(anImport.getQualid().printTrimmed()).matches()) { imports.add(anImport); return true; } return false; } @Override public void reset() { imports.clear(); } private static final J.Import SPACER = new J.Import(randomId(), new J.FieldAccess(randomId(), J.Ident.build(randomId(), "_SPACER", null, Formatting.EMPTY), J.Ident.build(randomId(), "_SPACER", null, Formatting.EMPTY), null, Formatting.EMPTY), false, Formatting.EMPTY); @Override public List orderedImports() { imports.sort(IMPORT_SORTING); // simplifies the logic of dealing with folding the last group of imports imports.add(SPACER); boolean foundStar = false; int consecutiveSamePackages = 0; for (int i = 0; i < imports.size(); i++) { consecutiveSamePackages++; if (i >= 1 && !imports.get(i - 1).getPackageName().equals(imports.get(i).getPackageName())) { int threshold = statik ? nameCountToUseStarImport : classCountToUseStarImport; if (consecutiveSamePackages >= threshold || (consecutiveSamePackages > 1 && foundStar)) { int j = i - 1; for (; j > i - consecutiveSamePackages + 1; j--) { imports.remove(j); } J.Import toStar = imports.get(j); if (!toStar.getQualid().getSimpleName().equals("*")) { imports.set(j, toStar.withQualid(toStar.getQualid().withName(toStar.getQualid() .getName().withName("*")))); } i = j + 1; } consecutiveSamePackages = 1; foundStar = false; } if ("*".equals(imports.get(i).getQualid().getSimpleName())) { foundStar = true; } } imports.remove(SPACER); return imports; } } class AllOthers extends ImportPackage { private final boolean statik; private Collection packageImports = emptyList(); public AllOthers(boolean statik, int classCountToUseStarImport, int nameCountToUseStarImport) { super(statik, "*", true, classCountToUseStarImport, nameCountToUseStarImport); this.statik = statik; } public void setPackageImports(Collection packageImports) { this.packageImports = packageImports; } public boolean isStatic() { return statik; } @Override public boolean accept(J.Import anImport) { if (packageImports.stream().noneMatch(pi -> pi.accept(anImport))) { return super.accept(anImport); } return false; } } } public Validated validate() { return valid("layout", blocks.stream() .filter(b -> b instanceof Block.AllOthers && !((Block.AllOthers) b).isStatic()) .count() == 1); } public static Builder builder(int classCountToUseStarImport, int nameCountToUseStarImport) { return new Builder(classCountToUseStarImport, nameCountToUseStarImport); } public static class Builder { private final List blocks = new ArrayList<>(); private final int classCountToUseStarImport; private final int nameCountToUseStarImport; public Builder(int classCountToUseStarImport, int nameCountToUseStarImport) { this.classCountToUseStarImport = classCountToUseStarImport; this.nameCountToUseStarImport = nameCountToUseStarImport; } public Builder importAllOthers() { blocks.add(new Block.AllOthers(false, classCountToUseStarImport, nameCountToUseStarImport)); return this; } public Builder importStaticAllOthers() { blocks.add(new Block.AllOthers(true, classCountToUseStarImport, nameCountToUseStarImport)); return this; } public Builder blankLine() { if (!blocks.isEmpty() && blocks.get(blocks.size() - 1) instanceof Block.BlankLines) { ((Block.BlankLines) blocks.get(blocks.size() - 1)).count++; } else { blocks.add(new Block.BlankLines()); } return this; } public Builder importPackage(String packageWildcard, boolean withSubpackages) { blocks.add(new Block.ImportPackage(false, packageWildcard, withSubpackages, classCountToUseStarImport, nameCountToUseStarImport)); return this; } public Builder importPackage(String packageWildcard) { return importPackage(packageWildcard, true); } public Builder staticImportPackage(String packageWildcard, boolean withSubpackages) { blocks.add(new Block.ImportPackage(true, packageWildcard, withSubpackages, classCountToUseStarImport, nameCountToUseStarImport)); return this; } public Builder staticImportPackage(String packageWildcard) { return staticImportPackage(packageWildcard, true); } public Layout build() { for (Block block : blocks) { if (block instanceof Block.AllOthers) { ((Block.AllOthers) block).setPackageImports(blocks.stream() .filter(b -> b.getClass().equals(Block.ImportPackage.class)) .map(Block.ImportPackage.class::cast) .collect(toList())); } } return new Layout(blocks, classCountToUseStarImport, nameCountToUseStarImport); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy