java.nio.file パッケージでディレクトリ階層を走査する方法をあれこれ試してたら(Files クラスのメソッド 〜ディレクトリの階層を走査〜)、こちらにまるで天啓のようなお題があったので早速試してみました。
参考
- No Programming, No Life 「お題:package-info.javaの一括生成」
コード
さすが Java で書いただけあって、ちょっと長く、そしてちょっと助長かな(お前の書き方が悪いだけ、とツッコミを入れられそうだけど):
import java.util.Arrays; import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; public class PackageInfoGenerator extends SimpleFileVisitor<Path>{ private final Path root; private final Charset charset; PackageInfoGenerator(Path root, Charset charset){ this.root = root; this.charset = charset; } @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes atts) throws IOException { if(this.root.equals(dir)) return FileVisitResult.CONTINUE; // 起点となるディレクトリは無視 Path rel = this.root.relativize(dir); String packageName = createPackageName(rel); String[] content = { "/**", " * "+packageName+"パッケージ。", " *", " * <pre>", " * // TODO パッケージ内容の詳細を記述してください", " * </pre>", " * ", " */", "package "+packageName+";" }; Path packageInfo = dir.resolve("package-info.java"); Files.write(packageInfo, Arrays.asList(content), this.charset); System.out.println("[CREATE] "+packageInfo); return FileVisitResult.CONTINUE; } /** あえて Path が Iterable<Path> を実装しているのを使ってパッケージ名を作成 */ private String createPackageName(Path path){ String pkg = "", sep = ""; for(Path p : path){ pkg += sep+p; sep = "."; } return pkg; } /** * @param args 第1引数は起点となるディレクトリ、第2引数は出力ファイルのエンコーディング */ public static void main(String... args)throws IOException{ String rootPath = (args.length >= 1) ? args[0] : "./src"; Charset charset = (args.length >= 2) ? Charset.forName(args[1]) : Charset.defaultCharset(); Path root = Paths.get(rootPath); Files.walkFileTree(root, new PackageInfoGenerator(root, charset)); } }
- 起点となるディレクトリ(./src)には package-info.java は出力してはいけないんだよね(デフォルト・パッケージだし)
- ファイルへの書き出しに Files#write() を使ってますが(「Files クラスのメソッド 〜ファイル内容の読み書き〜」参照)、明示的にキャラクターセット(エンコーディング)を指定しないといけないので、main() メソッドの第2引数で指定できるようにしました
- ディレクトリ階層からパッケージ名を生成する際(createPackageName() メソッド)、あえて Path インターフェースが Iterable<Path> を実装していることを使ってみました。 ちなみに、列挙されるのはファイル・セパレータ(/ とか \)で隔てられた文字列です(「Path インターフェース, Paths クラス」参照)。