しがないSIerのシステムエンジニアとして日銭を稼ぐ毎日を送っているのですが、「作成したJavaクラスをパッケージ名を付与して一覧化する」という設計書(?)を作る機会がここ最近ありました。作ったJavaクラスの数からいって手作業はめんどくさいというか無謀な話。そこで「パッケージを再帰的に探索して、パッケージ名を付与したクラス一覧を出力する」プログラムを書くことにしました。以下はそのサンプルです。
たとえば上記のようなパッケージ構造のプロジェクトがあったとして、"jp.co"配下に属するクラスをパッケージ名付きで出力するプログラムは次のようになるはずです。
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Enumeration;
import java.util.PriorityQueue;
public class Main {
private static final String PACKAGE_SEPARATOR = ".";
private static final String CLASS_SUFFIX = ".class";
public static void main(String[] args) throws IOException, URISyntaxException {
// クラスローダを利用して、パッケージ配下のリソースを取得する。
String rootPackageName = "jp.co".replace(PACKAGE_SEPARATOR, File.separator);
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
Enumeration<URL> rootUrls = classLoader.getResources(rootPackageName);
// ディレクトリを再帰的に探索して、".class"で終わるファイルを見つけた場合は
// 文字列を整形したのちにリストへ格納しておく。
PriorityQueue<String> classNames = new PriorityQueue();
while (rootUrls.hasMoreElements()) {
URL rootUrl = rootUrls.nextElement();
Path rootPath = Paths.get(rootUrl.toURI());
Files.walkFileTree(rootPath, new SimpleFileVisitor<Path>(){
@Override
public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException {
String pathName = path.toString();
if (pathName.endsWith(CLASS_SUFFIX)) {
int beginIndex = pathName.lastIndexOf(rootPackageName);
int endIndex = pathName.lastIndexOf(CLASS_SUFFIX);
String className = pathName.substring(beginIndex, endIndex)
.replace(File.separator, PACKAGE_SEPARATOR);
classNames.add(className);
}
return super.visitFile(path, attrs);
}
});
}
// 見つけ出したクラス名の一覧を出力する。
for (String className : classNames) {
System.out.println(className);
}
/*
jp.co.first.ClassA
jp.co.first.ClassB
jp.co.first.sub.ClassC
jp.co.first.sub.ClassD
jp.co.second.ClassE
jp.co.second.ClassF
*/
}
}