package io.github.linwancen.code.modify;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* 刪除未使用的類
* <br>僅依賴 JDK
*/
public class DeleteNotUsage {
/**
* 多線程電腦會很卡,約快 3 倍
*/
private static final boolean PARALLEL = true;
private static final boolean DELETE = false;
private static final String CLASS_PATH = ClassLoader.getSystemResource("").getPath();
/**
* 掃描根目錄
* <br>根據(jù)需要添加.getParentFile()
*/
private static final File ROOT;
static {
if (CLASS_PATH.endsWith("test-classes")) {
// maven
ROOT= new File(CLASS_PATH) // target/test-classes
.getParentFile() // target
.getParentFile(); //
} else if (CLASS_PATH.endsWith("main")) {
// gradle
ROOT = new File(CLASS_PATH) // main
.getParentFile() // java
.getParentFile() // classes
.getParentFile() // build
.getParentFile();
} else {
ROOT = new File("");
}
}
private static final int ROOT_LEN = ROOT.getPath().length();
private static final Pattern EXT_PATTERN = Pattern.compile("\\.(?:java|xml|properties|conf|yml)|services$");
private static final Pattern EXCLUDE_PATTERN = Pattern.compile("target|.git");
/**
* 添加自行定義的會被調用到的注解或關鍵字
*/
private static final Pattern USED_PATTERN = Pattern.compile(
"@(?:Service|Repository|Component|Test|Controller|Configuration)|main\\(String");
private static final AtomicInteger N = new AtomicInteger();
private static final class Data {
final String pathString;
final Set<String> usages = new ConcurrentSkipListSet<>();
final File file;
public Data(String pathString, File file) {
this.pathString = pathString;
this.file = file;
}
}
public static void main(String[] args) throws Exception {
List<Path> paths = Files.walk(ROOT.toPath())
.filter(path -> path.toFile().isFile())
.filter(path -> {
String s = path.toString();
return EXT_PATTERN.matcher(s).find() && !EXCLUDE_PATTERN.matcher(s).find();
})
.collect(Collectors.toList());
int fileCount = paths.size();
System.out.println("walk complete:" + fileCount);
Map<String, Data> usageMap = new HashMap<>();
StringBuilder patternStr = new StringBuilder("\\b(?:");
for (Path path : paths) {
filterClassSimpleName(usageMap, patternStr, path);
}
System.out.println("filter complete:" + usageMap.size());
int len = patternStr.length();
if (len == 0) {
return;
}
patternStr.delete(len - 1, len);
patternStr.append(")\\b");
Pattern pattern = Pattern.compile(patternStr.toString());
System.out.println("pattern compile complete:" + patternStr.length());
long start = System.currentTimeMillis();
if (PARALLEL) {
paths.parallelStream().forEach(path ->
check(fileCount, usageMap, pattern, start, N.incrementAndGet(), path));
} else {
for (int i = 0; i < fileCount; i++) {
Path path = paths.get(i);
check(fileCount, usageMap, pattern, start, i, path);
}
}
long useTime = (System.currentTimeMillis() - start) / 1000;
System.out.println("check complete, size:{}" + fileCount + " use " + useTime / 60 + ":" + useTime % 60);
long count = usageMap.entrySet().stream()
.filter(entry -> {
if (!entry.getValue().usages.isEmpty()) {
return false;
}
String symbol = "-";
if (DELETE) {
symbol = entry.getValue().file.delete() ? "√" : "×";
}
String path = entry.getValue().pathString.substring(ROOT_LEN);
System.out.println(symbol + "\t.(" + entry.getKey() + ".java:0)\t" + path);
return true;
})
.count();
System.out.println("check print complete, " + count + "/" + usageMap.size());
}
private static void filterClassSimpleName(Map<String, Data> usageMap, StringBuilder patternStr, Path path) {
String s = path.toString();
if (!s.endsWith(".java")) {
return;
}
try {
byte[] bytes = Files.readAllBytes(path);
String code = new String(bytes, StandardCharsets.UTF_8);
if (USED_PATTERN.matcher(code).find()) {
return;
}
} catch (IOException e) {
e.printStackTrace();
return;
}
String classSimpleName = s.substring(s.lastIndexOf(File.separator) + 1, s.length() - 5);
usageMap.put(classSimpleName, new Data(s, path.toFile()));
patternStr.append(classSimpleName).append("|");
}
private static void check(int fileCount, Map<String, Data> usageMap, Pattern pattern, long start, int i, Path p) {
if (i != 0 && (i % 1000) == 0) {
long use = System.currentTimeMillis() - start;
long remain = use / i * (fileCount - i);
use = use / 1000;
remain = remain / 1000;
System.out.println("check " + i + "/" + fileCount
+ ", use " + use / 60 + ":" + use % 60
+ ", remain " + remain / 60 + ":" + remain % 60);
}
String pathStr = p.toString();
try {
byte[] bytes = Files.readAllBytes(p);
String code = new String(bytes, StandardCharsets.UTF_8);
Matcher m = pattern.matcher(code);
while (m.find()) {
String s = m.group();
Data data = usageMap.get(s);
if (!data.pathString.equals(pathStr)) {
data.usages.add(pathStr);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
刪除未使用的Java類文件
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
相關閱讀更多精彩內容
- 歡迎訪問我的主頁:http://catchzeng.comQQ群:157672725 前言 隨著團隊人數(shù)增多,工程...
- 隨著項目版本不斷的迭代開發(fā),項目中的圖片資源文件也不斷的替換,如果沒有養(yǎng)成一個好的使用習慣,時間久了,就會容易產生...
- 查看docker磁盤使用情況 刪除所有未使用的容器,網絡,鏡像(沒有被使用的 image)以及緩存。 刪除未被正在...
- 一、查找項目中未使用的類 工具下載地址:https://github.com/dblock/fui 使用方法 第一...