ファイルのリスト表示と連想配列の選択ユティリティーを利用してファイルを選択するユーティリティーを作りたいと思います。
機能を整理する
/Users/myaccount/myworkディレクトリから下のファイル構成が以下のようになっているものとします。
. ├── dir_1 │ ├── file_d.txt │ └── file_e.txt ├── file_a.txt ├── file_b.txt └── file_c.txt
このときfile_a.txt、file_b.txt、file_c.txtの他にfile_d.txtとfile_e.txtも選べるようにします。ただ一度に全てのファイルを表示するとファイルの数が多くなるにつれ見つけにくくなってしまいます。最初の選択肢にはile_d.txtとfile_e.txtは表示させず代わりにディレクトリdir_1を選べるようにしておきます。もしdir_1が選択されたらdir_1の中のファイルを表示し再び選択させるようにします。このようにすればサブディレクトリが増え何層もの構造になっても深いところにあるファイルを選択することができます。
まずサブディレクトリを含めてファイルを表示するコードをもう一度見て見ましょう。
File mywork = new File("/Users/myaccount/mywork");
FilenameFilter filter = new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
if (name.toLowerCase().equals(".ds_store")) {
return false;
}
return true;
}
};
File[] files = mywork.listFiles(filter);
for (File file : files) {
String sign;
if(file.isFile()){
sign = "[f]";
}else if(file.isDirectory()){
sign = "[d]";
}else{
sign = "[?]";
}
System.out.println(sign + " " + file.getName());
}
この後半の表示ロジックの代わりに連想配列を作成して選択させるようにします。選ぶだけなら通常の配列の順番を選択させるようにすれば良いのですが将来的に「上のディレクトリに戻る」や「キャンセル」のような配列以外の選択肢をつけたいので連想配列にしています。
実装する
連想配列はユーティリティー選択用のための値が文字列のものと呼び出し元で保持する値がFileのものを作ります。同じキーを使っているので連想配列選択で帰ってきたキーを元に値がFileの連想配列からファイルを取り出すことができます。
public File readFile(File dir) {
FilenameFilter filter = new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
if (name.toLowerCase().equals(".ds_store")) {
return false;
}
return true;
}
};
File[] files = dir.listFiles(filter);
LinkedHashMap<String, String> str_map = new LinkedHashMap<>();
LinkedHashMap<String, File> file_map = new LinkedHashMap<>();
for (int i = 0;i < files.length;i++) {
String sign;
if (files[i].isFile()) {
sign = "[f]";
} else if (files[i].isDirectory()) {
sign = "[d]";
} else {
sign = "[?]";
}
str_map.put(Integer.toString(i), sign + " " + files[i].getName());
file_map.put(Integer.toString(i), files[i]);
}
String key = readKey(dir.getAbsolutePath(), "ファイルを選んでください", str_map);
File file = file_map.get(key);
if(file.isDirectory()){
// 選択されたものがディレクトリである場合はそのディレクトリを引数にして自分自身を呼び出す。
return readFile(file);
}
return file;
}
ここで注意するところは選択されたものがディレクトリである場合そのディレクトリを引数としてもう一度このメソッド自身を呼び出しているところです。これは動かして見ないとわかりにくいと思いますのでこの呼び出し処理を作り動かします。
TerminalUtil util = new TerminalUtil();
File file = util.readFile(new File("/Users/myaccount/mywork"));
System.out.println("選ばれたファイルは" + file.getAbsolutePath() + "です。");
まず最初のディレクトリにあるファイルを選択した場合の実行結果です。
[/Users/myaccount/mywork] 0:[d] dir_1 1:[f] file_a.txt 2:[f] file_b.txt 3:[f] file_c.txt ファイルを選んでください>1 選ばれたファイルは/Users/myaccount/mywork/file_a.txtです。
次にディレクトリを指定してからその下のファイルを選択した場合の実行結果です。
[/Users/myaccount/mywork] 0:[d] dir_1 1:[f] file_a.txt 2:[f] file_b.txt 3:[f] file_c.txt ファイルを選んでください>0 [/Users/myaccount/mywork/dir_1] 0:[f] file_d.txt 1:[f] file_e.txt ファイルを選んでください>0 選ばれたファイルは/Users/myaccount/mywork/dir_1/file_d.txtです。
このように再帰的にメソッドを呼び出すと複雑なディレクトリ構成の場合でも柔軟にファイルの選択を促すことができます。