2 * configuration file & directory
4 * License : The MIT License
5 * Copyright(c) 2009 olyutorskii
8 package jp.sfjp.jindolf.config;
10 import java.io.BufferedInputStream;
12 import java.io.IOException;
13 import java.io.InputStream;
14 import java.nio.file.Files;
15 import java.nio.file.Path;
16 import java.nio.file.Paths;
17 import java.text.MessageFormat;
18 import java.util.logging.Level;
19 import java.util.logging.Logger;
20 import javax.swing.JDialog;
21 import javax.swing.JOptionPane;
22 import jp.sfjp.jindolf.ResourceManager;
23 import jp.sfjp.jindolf.VerInfo;
24 import jp.sfjp.jindolf.view.LockErrorPane;
27 * Jindolf設定格納ディレクトリに関するあれこれ。
29 public final class ConfigDirUtils{
31 private static final Logger LOGGER = Logger.getAnonymousLogger();
33 private static final String TITLE_BUILDCONF =
34 VerInfo.TITLE + "設定格納ディレクトリの設定";
36 private static final Path JINCONF = Paths.get("Jindolf");
37 private static final Path JINCONF_DOT = Paths.get(".jindolf");
38 private static final Path FILE_README = Paths.get("README.txt");
39 private static final Path FILE_AVATARJSON = Paths.get("avatarCache.json");
41 private static final String RES_DIR = "resources";
42 private static final String RES_README = RES_DIR + "/README.txt";
43 private static final String RES_IMGDIR = RES_DIR + "/image";
44 private static final String RES_AVATARJSON = RES_IMGDIR + "/avatarCache.json";
46 private static final String MSG_POST =
48 + "<li><code>" + CmdOption.OPT_CONFDIR + "</code>"
49 + " オプション指定により、<br/>"
50 + "任意の設定格納ディレクトリを指定することができます。<br/>"
51 + "<li><code>" + CmdOption.OPT_NOCONF + "</code>"
52 + " オプション指定により、<br/>"
53 + "設定格納ディレクトリを使わずに起動することができます。<br/>"
56 private static final int ERR_ABORT = 1;
62 private ConfigDirUtils(){
72 private static void abort(){
73 System.exit(ERR_ABORT);
79 * ダイアログを表示し、閉じられるまで待つ。
81 * @param pane ダイアログの元となるペイン
83 private static void showDialog(JOptionPane pane){
84 JDialog dialog = pane.createDialog(TITLE_BUILDCONF);
85 dialog.setResizable(true);
88 dialog.setVisible(true);
96 * 共通エラーメッセージ確認ダイアログを表示する。
102 private static void showErrorMessage(String txt){
104 pane = new JOptionPane(txt, JOptionPane.ERROR_MESSAGE);
111 * 共通エラーメッセージ確認ダイアログを表示する。
115 * @param seq メッセージtxt
117 private static void showWarnMessage(String txt){
119 pane = new JOptionPane(txt, JOptionPane.WARNING_MESSAGE);
126 * 情報提示メッセージ確認ダイアログを表示する。
130 * @param seq メッセージtxt
132 private static void showInfoMessage(String txt){
134 pane = new JOptionPane(txt, JOptionPane.INFORMATION_MESSAGE);
140 * センタリングされたファイル名表示のHTML表記を出力する。
145 public static String getCenteredFileName(Path path){
146 String form = "<center>[ {0} ]</center><br/>";
147 String fileName = FileUtils.getHtmledFileName(path);
148 String result = MessageFormat.format(form, fileName);
153 * 設定ディレクトリ生成をやめた操作への警告をダイアログで提示し、
156 private static void abortQuitBuildConfigDir(){
159 + "設定ディレクトリの作成をせずに起動を中止します。<br/>"
163 showWarnMessage(msg);
171 * ディレクトリが生成できないエラーをダイアログで提示し、
174 * @param path 生成できなかったディレクトリ
176 private static void abortCantBuildDir(Path path){
185 String fileName = getCenteredFileName(path);
186 String msg = MessageFormat.format(form, fileName);
188 showErrorMessage(msg);
196 * ディレクトリへアクセスできないエラーをダイアログで提示し、
199 * @param path アクセスできないディレクトリ
201 private static void abortCantAccessDir(Path path){
208 + "このディレクトリへのアクセス権を調整し"
209 + "読み書きできるようにしてください。<br/>"
212 String fileName = getCenteredFileName(path);
213 String msg = MessageFormat.format(form, fileName);
215 showErrorMessage(msg);
223 * ファイルに書き込めないエラーをダイアログで提示し、VM終了する。
225 * @param path 書き込めなかったファイル
227 private static void abortCantWrite(Path path){
235 String fileName = getCenteredFileName(path);
236 String msg = MessageFormat.format(form, fileName);
238 showErrorMessage(msg);
246 * 設定ディレクトリがアクセス可能でなければ
247 * エラーダイアログを出してVM終了する。
249 * @param confDir 設定ディレクトリ
251 public static void checkDirPerm(Path confDir){
252 if( ! FileUtils.isAccessibleDirectory(confDir) ){
253 abortCantAccessDir(confDir);
260 * アプリケーション設定ディレクトリを返す。
262 * <p>存在の有無、アクセスの可否は関知しない。
264 * <p>WindowsやLinuxではホームディレクトリ。
265 * Mac OS X ではさらにホームディレクトリの下の
266 * "Library/Application Support/"
268 * @return アプリケーション設定ディレクトリ
270 public static Path getAppSetDir(){
271 Path home = FileUtils.getHomeDirectory();
275 if(FileUtils.isMacOSXFs()){
276 result = result.resolve("Library");
277 result = result.resolve("Application Support");
288 * <li>起動元JARファイルと同じディレクトリに、
289 * アクセス可能なディレクトリ"Jindolf"が
292 * <li>起動元JARファイルおよび"Jindolf"が発見できなければ、
293 * MacOSX環境の場合"~/Library/Application Support/Jindolf/"を返す。
294 * Windows環境の場合"%USERPROFILE%\Jindolf\"を返す。
296 * <li>それ以外の環境(Linux,etc?)の場合"~/.jindolf/"を返す。
300 * <p>返すディレクトリが存在しているか否か、
301 * アクセス可能か否かは呼び出し元で判断せよ。
305 public static Path getDefaultConfDirPath(){
306 Path jarParent = FileUtils.getJarDirectory();
307 if(FileUtils.isAccessibleDirectory(jarParent)){
308 Path confPath = jarParent.resolve(JINCONF);
309 if(FileUtils.isAccessibleDirectory(confPath)){
314 Path appset = getAppSetDir();
317 if(FileUtils.isMacOSXFs() || FileUtils.isWindowsOSFs()){
318 result = appset.resolve(JINCONF);
320 result = appset.resolve(JINCONF_DOT);
327 * まだ存在しない設定格納ディレクトリを新規に作成する。
329 * <p>エラーがあればダイアログ提示とともにVM終了する。
333 * @param confPath 設定格納ディレクトリ
334 * @return 新規に作成した設定格納ディレクトリの絶対パス
336 public static Path buildConfDirPath(Path confPath){
337 Path absPath = confPath.toAbsolutePath();
338 if(Files.exists(absPath)) return absPath;
340 boolean confirmed = confirmBuildConfigDir(absPath);
342 abortQuitBuildConfigDir();
347 Files.createDirectories(absPath);
348 }catch(IOException | SecurityException e){
349 String msg = MessageFormat.format(
350 "{0}を生成できません", absPath.toString());
351 LOGGER.log(Level.SEVERE, msg, e);
352 abortCantBuildDir(absPath);
356 // FileUtils.setOwnerOnlyAccess(absPath);
358 checkDirPerm(absPath);
360 touchReadme(absPath);
366 * 設定ディレクトリを新規に生成してよいかダイアログで問い合わせる。
368 * @param confDir 設定ディレクトリ
369 * @return 生成してよいと指示があればtrue
371 private static boolean confirmBuildConfigDir(Path confDir){
374 + "設定ファイル格納ディレクトリ<br/>"
376 + "このディレクトリを今から作成して構いませんか?<br/>"
377 + "このディレクトリ名は、後からいつでもヘルプウィンドウで<br/>"
380 String confName = getCenteredFileName(confDir);
381 String msg = MessageFormat.format(form, confName);
384 pane = new JOptionPane(msg,
385 JOptionPane.QUESTION_MESSAGE,
386 JOptionPane.YES_NO_OPTION);
389 Object val = pane.getValue();
390 if( ! (val instanceof Integer) ) return false;
391 int ival = (int) val;
392 boolean result = ival == JOptionPane.YES_OPTION;
398 * ローカル画像キャッシュディレクトリを作る。
401 * ファイルavatarCache.jsonが作られる。
403 * @param imgCacheDir ローカル画像キャッシュディレクトリ
405 public static void buildImageCacheDir(Path imgCacheDir){
406 Path absPath = imgCacheDir.toAbsolutePath();
407 if(Files.exists(absPath)) return;
410 Files.createDirectories(absPath);
411 }catch(IOException e){
412 String msg = MessageFormat.format(
413 "ディレクトリ{0}を生成できません", absPath.toString());
414 LOGGER.log(Level.SEVERE, msg, e);
415 abortCantBuildDir(absPath);
419 checkDirPerm(absPath);
421 Path jsonPath = imgCacheDir.resolve(FILE_AVATARJSON);
423 try(InputStream ris =
424 ResourceManager.getResourceAsStream(RES_AVATARJSON)){
425 InputStream is = new BufferedInputStream(ris);
426 Files.copy(is, jsonPath);
427 }catch(IOException e){
428 String msg = MessageFormat.format(
429 "ファイル{0}を生成できませんでした", jsonPath.toString()
431 LOGGER.log(Level.SEVERE, msg, e);
432 abortCantWrite(jsonPath);
442 * <p>呼び出しから戻ってもまだロックオブジェクトが
444 * 今後設定ディレクトリは一切使わずに起動を続行するものとする。
446 * <p>ロックファイルの強制解除に失敗した場合はVM終了する。
448 * @param lock エラーを起こしたロック
450 public static void confirmLockError(InterVMLock lock){
451 File lockFile = lock.getLockFile();
452 LockErrorPane lockPane = new LockErrorPane(lockFile.toPath());
453 JDialog lockDialog = lockPane.createDialog(TITLE_BUILDCONF);
454 lockDialog.setResizable(true);
458 lockDialog.setVisible(true);
460 Object result = lockPane.getValue();
461 boolean aborted = LockErrorPane.isAborted(result);
462 boolean windowClosed = result == null;
464 if(aborted || windowClosed){
468 }else if(lockPane.isRadioRetry()){
470 if(lock.isFileOwner()) break;
471 }else if(lockPane.isRadioContinue()){
474 + "設定ディレクトリを使わずに起動を続行します。<br/>"
475 + "今回、各種設定の読み込み・保存はできません。<br/>"
477 + CmdOption.OPT_NOCONF
479 + "を使うとこの警告は出なくなります。"
481 showInfoMessage(msg);
483 }else if(lockPane.isRadioForce()){
485 if(lock.isExistsFile()){
488 + "ロックファイルの強制解除に失敗しました。<br/>"
490 + "が見つからないのであれば、<br/>"
491 + "なんとかしてロックファイル<br/>"
496 String fileName = getCenteredFileName(lockFile.toPath());
497 String msg = MessageFormat.format(form, fileName);
499 showErrorMessage(msg);
506 if(lock.isFileOwner()) break;
512 + "を確保することができません。<br/>"
515 String fileName = getCenteredFileName(lockFile.toPath());
516 String msg = MessageFormat.format(form, fileName);
518 showErrorMessage(msg);
526 lockDialog.dispose();
532 * 指定されたディレクトリにREADMEファイルを生成する。
534 * <p>生成できなければダイアログ表示とともにVM終了する。
536 * @param path READMEの格納ディレクトリ
538 private static void touchReadme(Path path){
539 Path readme = path.resolve(FILE_README);
541 try(InputStream ris =
542 ResourceManager.getResourceAsStream(RES_README)){
543 InputStream is = new BufferedInputStream(ris);
544 Files.copy(is, readme);
545 }catch(IOException e){
546 String msg = MessageFormat.format(
547 "{0}が生成できませんでした", readme.toString()
549 LOGGER.log(Level.SEVERE, msg, e);
550 abortCantWrite(readme);