1 package charactermanaj.model;
\r
3 import java.awt.Color;
\r
4 import java.io.BufferedOutputStream;
\r
6 import java.io.FileNotFoundException;
\r
7 import java.io.FileOutputStream;
\r
8 import java.io.IOException;
\r
9 import java.io.InputStream;
\r
10 import java.io.OutputStream;
\r
11 import java.net.URI;
\r
12 import java.net.URL;
\r
13 import java.nio.charset.Charset;
\r
14 import java.util.Properties;
\r
15 import java.util.Set;
\r
16 import java.util.logging.Level;
\r
17 import java.util.logging.Logger;
\r
18 import java.util.regex.Pattern;
\r
20 import charactermanaj.util.ApplicationLogHandler;
\r
21 import charactermanaj.util.BeanPropertiesUtilities;
\r
22 import charactermanaj.util.ConfigurationDirUtilities;
\r
25 * アプリケーションの全域にわたる設定.<br>
\r
26 * アプリケーション設定は、クラスパス上のリソース、コートベース直下のappConfig.xml、ユーザーごとのappConfig.xmlの順に読み込まれます.<br>
\r
29 public final class AppConfig {
\r
34 private static final String CONFIG_NAME = "appConfig.xml";
\r
37 * 全ユーザー用キャラクターディレクトリのシステムプロパティのキー名.<br>
\r
39 public static final String COMMON_CHARACTER_DIR_PROPERTY_NAME = "character.dir";
\r
44 private static final String DEFAULT_SPECIFICATION_VERSION = "1.0";
\r
50 private static final Logger logger = Logger.getLogger(AppConfig.class.getName());
\r
56 private static final AppConfig singleton = new AppConfig();
\r
63 public static AppConfig getInstance() {
\r
70 private AppConfig() {
\r
74 private String implementationVersion;
\r
76 private String specificationVersion;
\r
80 * ビルドされたjarパッケージからバージョン情報を取得する.<br>
\r
81 * クラスパスの実行からのバージョンは常に「develop」となる.<br>
\r
84 public String getImplementationVersion() {
\r
85 return implementationVersion;
\r
90 * ビルドされたjarパッケージからバージョン情報を取得する.<br>
\r
91 * クラスパスの実行からのバージョンは常に「develop」となる.<br>
\r
94 public String getSpecificationVersion() {
\r
95 return specificationVersion;
\r
99 * ビルドされたjarパッケージからバージョン情報を取得する.<br>
\r
100 * クラスパスの実行からのバージョンは常に「develop」となる.<br>
\r
102 private void loadAppVersions() {
\r
103 Package pak = this.getClass().getPackage();
\r
104 String implementationVersion = "develop";
\r
105 String specificationVersion = DEFAULT_SPECIFICATION_VERSION;
\r
107 String vInfo = pak.getImplementationVersion();
\r
108 if (vInfo != null && implementationVersion.trim().length() > 0) {
\r
109 implementationVersion = vInfo.trim();
\r
111 String specVInfo = pak.getSpecificationVersion();
\r
112 if (specVInfo != null && specVInfo.trim().length() > 0) {
\r
113 specificationVersion = specVInfo.trim();
\r
117 this.implementationVersion = implementationVersion;
\r
118 this.specificationVersion = specificationVersion;
\r
123 * リソース上の/appConfig.xml、コードベース下のappConfig.xml、アプリケーションデータ保存先のappConfig.xmlの順に読み取る.<br>
\r
124 * 存在しないか、読み取りに失敗した場合は、該当ファイルはスキップされる.<br>
\r
126 public void loadConfig() {
\r
127 Properties config = new Properties();
\r
129 File codeBase = ConfigurationDirUtilities.getApplicationBaseDir();
\r
130 File userDataDir = ConfigurationDirUtilities.getUserDataDir();
\r
132 new File(getClass().getResource("/" + CONFIG_NAME).getPath()).toURI(),
\r
133 new File(codeBase, CONFIG_NAME).getCanonicalFile().toURI(),
\r
134 new File(userDataDir, CONFIG_NAME).toURI(),
\r
136 for (URI uri : uris) {
\r
138 continue; // リソースがない場合はnullになる
\r
140 // ファイルの実在チェック (チェックできる場合のみ)
\r
141 if ("file".equals(uri.getScheme())) {
\r
142 File file = new File(uri);
\r
143 if (!file.exists()) {
\r
144 logger.log(Level.INFO, "appConfig.xml is not found.:" + file);
\r
148 // appConfig.xmlの読み込みを行う.
\r
149 // Properties#loadFromXML() はXMLからキーを読み取り、既存のキーに対して上書きする.
\r
150 // XMLに存在しないキーは読み込み前のままなので、繰り返し呼び出すことで「重ね合わせ」することができる.
\r
152 URL resourceURL = uri.toURL();
\r
153 InputStream is = resourceURL.openStream();
\r
155 config.loadFromXML(is);
\r
156 logger.log(Level.INFO, "appConfig.xml is loaded.:" + uri);
\r
161 } catch (FileNotFoundException ex) {
\r
162 logger.log(Level.INFO, "appConfig.xml is not found.: " + uri, ex);
\r
163 // 無視する (無い場合は十分にありえるので「情報」レベルでログ。)
\r
164 } catch (Exception ex) {
\r
165 logger.log(Level.WARNING, "appConfig.xml loading failed.: " + uri, ex);
\r
170 } catch (IOException ex) {
\r
171 throw new RuntimeException("appConfig.xml loading failed.", ex);
\r
172 } catch (RuntimeException ex) {
\r
173 throw new RuntimeException("appConfig.xml loading failed.", ex);
\r
175 BeanPropertiesUtilities.loadFromProperties(this, config);
\r
179 * プロパティをアプリケーションデータの保存先に保存する.
\r
180 * @throws IOException 保存に失敗した場合
\r
182 public void saveConfig() throws IOException {
\r
183 Properties config = getProperties();
\r
185 File userDataDir = ConfigurationDirUtilities.getUserDataDir();
\r
186 File configStore = new File(userDataDir, CONFIG_NAME);
\r
187 OutputStream os = new BufferedOutputStream(new FileOutputStream(configStore));
\r
189 config.storeToXML(os, CONFIG_NAME, "UTF-8");
\r
196 * Propertiesの値を設定した場合に設定できない項目があるかチェックする.<br>
\r
197 * このメソッドを呼び出しても、アプリケーション設定自身は何も影響されない.<br>
\r
198 * @param props 適用するプロパティ
\r
199 * @return 設定できなかったプロパティキーのコレクション、問題なければ空が返される.
\r
201 public static Set<String> checkProperties(Properties props) {
\r
202 if (props == null) {
\r
203 throw new IllegalArgumentException();
\r
205 AppConfig dummy = new AppConfig(); // アプリケーションから参照されないダミーのインスタンスを作成する.
\r
206 return BeanPropertiesUtilities.loadFromProperties(dummy, props);
\r
210 * Propertiesの値で設定を更新する.<br>
\r
211 * @param props 適用するプロパティ
\r
212 * @return 設定できなかったプロパティキーのコレクション、問題なければ空が返される.
\r
214 public Set<String> update(Properties props) {
\r
215 if (props == null) {
\r
216 throw new IllegalArgumentException();
\r
218 return BeanPropertiesUtilities.loadFromProperties(this, props);
\r
222 * このアプリケーション設定をプロパティに書き出して返します.<br>
\r
225 public Properties getProperties() {
\r
226 Properties config = new Properties();
\r
227 BeanPropertiesUtilities.saveToProperties(this, config);
\r
232 * 全ユーザー共通のキャラクターデータディレクトリを取得する.<br>
\r
233 * 全ユーザー共通のキャラクターデータディレクトリが実在しない場合はnullを返す.<br>
\r
234 * アプリケーション設定、システムプロパティ「character.dir」、アプリケーションディレクトリ上の「characters」の優先順で検索される.<br>
\r
235 * @return 全ユーザー共通のキャラクターデータディレクトリ、またはnull
\r
237 public File getSystemCharactersDir() {
\r
239 File applicationBaseDir = ConfigurationDirUtilities.getApplicationBaseDir();
\r
240 File[] candidates = new File[] {
\r
241 getAbsoluteFile(applicationBaseDir, getCommonCharacterDataDir()),
\r
242 getAbsoluteFile(applicationBaseDir, System.getProperty(COMMON_CHARACTER_DIR_PROPERTY_NAME)),
\r
243 getAbsoluteFile(applicationBaseDir, "characters"),
\r
245 File systemCharacterDir = null;
\r
246 for (File dir : candidates) {
\r
247 // 候補を順に検査し最初に合格したディレクトリを採用する
\r
251 if (dir.exists() && dir.isDirectory()) {
\r
252 systemCharacterDir = dir;
\r
256 return systemCharacterDir;
\r
260 * 指定したファイルが絶対パスであれば、それを返す.<br>
\r
261 * 絶対パスでなければベースディレクトリを親とした相対パスとして正規化し、その絶対パスを返す.<br>
\r
262 * fileがnullまたは空文字の場合はnullを返す.<br>
\r
263 * baseDirとfileを連結し正規化するときにエラーが発生した場合はログに記録しnullを返す.<br>
\r
264 * @param baseDir ベースディレクトリ、fileが相対パスであれば、fileの親となる.
\r
265 * @param file ファイル、nullまたは空文字も可
\r
266 * @return 絶対パス、もしくはnull
\r
268 protected File getAbsoluteFile(File baseDir, String file) {
\r
269 if (baseDir == null) {
\r
270 throw new IllegalArgumentException();
\r
272 if (file == null || file.trim().length() == 0) {
\r
275 File result = new File(file);
\r
276 if (!result.isAbsolute()) {
\r
278 // ファイル名が絶対パスでない場合はベースディレクトリからの相対にする
\r
279 result = new File(baseDir, file.trim()).getCanonicalFile();
\r
281 } catch (IOException ex) {
\r
282 logger.log(Level.SEVERE, "invalid dir: base=" + baseDir + "/file=" + file, ex);
\r
290 * ユーザー固有のキャラクターデータディレクトリを取得する.<br>
\r
291 * ユーザー固有のキャラクターディレクトリがまだ存在しない場合は作成される.<br>
\r
292 * @return ユーザー固有のキャラクターデータディレクトリ
\r
294 public File getUserCharactersDir() {
\r
295 File characterBaseDir = new File(ConfigurationDirUtilities.getUserDataDir(), "characters");
\r
296 if (!characterBaseDir.exists()) {
\r
297 if (!characterBaseDir.mkdirs()) {
\r
298 logger.log(Level.WARNING, "can't create the charatcer base directory. " + characterBaseDir);
\r
301 return characterBaseDir;
\r
305 * 全ユーザー対象のキャラクターディレクトリを明示的に指定する場合のフォルダパス.<br>
\r
306 * 相対パス指定の場合はアプリケーションディレクトリからの相対となる.<br>
\r
307 * nullまたは空文字はシステムプロパティ、もしくはデフォルトディレクトリを使用することを意味する.<br>
\r
310 public String getCommonCharacterDataDir() {
\r
311 return commonCharacterDataDir;
\r
314 public void setCommonCharacterDataDir(String commonCharacterDataDir) {
\r
315 if (commonCharacterDataDir == null) {
\r
316 commonCharacterDataDir = "";
\r
318 this.commonCharacterDataDir = commonCharacterDataDir;
\r
321 private String commonCharacterDataDir = "";
\r
325 * プロファイル選択ダイアログのプロファイルのサンプルイメージの背景色
\r
326 * @return サンプルイメージの背景色
\r
328 public Color getSampleImageBgColor() {
\r
329 return sampleImageBgColor;
\r
332 public void setSampleImageBgColor(Color sampleImageBgColor) {
\r
333 if (sampleImageBgColor == null) {
\r
334 throw new IllegalArgumentException();
\r
336 this.sampleImageBgColor = sampleImageBgColor;
\r
339 private Color sampleImageBgColor = Color.white;
\r
343 * デフォルトのイメージ背景色を取得する.
\r
344 * @return デフォルトのイメージ背景色
\r
346 public Color getDefaultImageBgColor() {
\r
347 return defaultImageBgColor;
\r
350 public void setDefaultImageBgColor(Color defaultImageBgColor) {
\r
351 if (defaultImageBgColor == null) {
\r
352 throw new IllegalArgumentException();
\r
354 this.defaultImageBgColor = defaultImageBgColor;
\r
357 private Color defaultImageBgColor = Color.white;
\r
360 * 使用中アイテムの背景色を取得する.
\r
361 * @return 使用中アイテムの背景色
\r
363 public Color getCheckedItemBgColor() {
\r
364 return checkedItemBgColor;
\r
367 public void setCheckedItemBgColor(Color checkedItemBgColor) {
\r
368 if (checkedItemBgColor == null) {
\r
369 throw new IllegalArgumentException();
\r
371 this.checkedItemBgColor = checkedItemBgColor;
\r
374 private Color checkedItemBgColor = Color.cyan.brighter();
\r
379 * @return 選択アイテムの背景色
\r
381 public Color getSelectedItemBgColor() {
\r
382 return selectedItemBgColor;
\r
385 public void setSelectedItemBgColor(Color selectedItemBgColor) {
\r
386 this.selectedItemBgColor = selectedItemBgColor;
\r
389 private Color selectedItemBgColor = Color.orange;
\r
392 * 不備のあるデータ行の背景色を取得する.
\r
393 * @return 不備のあるデータ行の背景色
\r
395 public Color getInvalidBgColor() {
\r
396 return invalidBgColor;
\r
399 public void setInvalidBgColor(Color invalidBgColor) {
\r
400 if (invalidBgColor == null) {
\r
401 throw new IllegalArgumentException();
\r
403 this.invalidBgColor = invalidBgColor;
\r
406 private Color invalidBgColor = Color.red.brighter().brighter();
\r
409 * JPEG画像変換時の圧縮率を取得する.
\r
412 public float getCompressionQuality() {
\r
413 return compressionQuality;
\r
416 public void setCompressionQuality(float compressionQuality) {
\r
417 if (compressionQuality < .1f || compressionQuality > 1f) {
\r
418 throw new IllegalArgumentException();
\r
420 this.compressionQuality = compressionQuality;
\r
423 private float compressionQuality = .8f;
\r
426 * エクスポートウィザードのプリセットにパーツ不足時の警告色(前景色)を取得する.
\r
427 * @return エクスポートウィザードのプリセットにパーツ不足時の警告色(前景色)
\r
429 public Color getExportPresetWarningsForegroundColor() {
\r
430 return exportPresetWarningsForegroundColor;
\r
433 public void setExportPresetWarningsForegroundColor(
\r
434 Color exportPresetWarningsForegroundColor) {
\r
435 this.exportPresetWarningsForegroundColor = exportPresetWarningsForegroundColor;
\r
438 private Color exportPresetWarningsForegroundColor = Color.red;
\r
441 * JARファイル転送用バッファサイズ.<br>
\r
442 * @return JARファイル転送用バッファサイズ.
\r
444 public int getJarTransferBufferSize() {
\r
445 return jarTransferBufferSize;
\r
448 public void setJarTransferBufferSize(int jarTransferBufferSize) {
\r
449 if (jarTransferBufferSize <= 0) {
\r
450 throw new IllegalArgumentException();
\r
452 this.jarTransferBufferSize = jarTransferBufferSize;
\r
455 private int jarTransferBufferSize = 4096;
\r
458 * ZIPファイル名のエンコーディング.<br>
\r
459 * @return ZIPファイル名のエンコーディング.<br>
\r
461 public String getZipNameEncoding() {
\r
462 return zipNameEncoding;
\r
465 public void setZipNameEncoding(String zipNameEncoding) {
\r
466 if (zipNameEncoding == null) {
\r
467 throw new IllegalArgumentException();
\r
470 Charset.forName(zipNameEncoding);
\r
471 } catch (Exception ex) {
\r
472 throw new RuntimeException("unsupported charset: " + zipNameEncoding);
\r
474 this.zipNameEncoding = zipNameEncoding;
\r
477 private String zipNameEncoding = "csWindows31J";
\r
480 * ディセーブルなテーブルのセルのフォアグラウンドカラーを取得する.
\r
481 * @return ディセーブルなテーブルのセルのフォアグラウンドカラー
\r
483 public Color getDisabledCellForgroundColor() {
\r
484 return disabledCellForegroundColor;
\r
487 public void setDisabledCellForegroundColor(Color disabledCellForegroundColor) {
\r
488 if (disabledCellForegroundColor == null) {
\r
489 throw new IllegalArgumentException();
\r
491 this.disabledCellForegroundColor = disabledCellForegroundColor;
\r
494 private Color disabledCellForegroundColor = Color.gray;
\r
498 * ディレクトリを監視する間隔(mSec)を取得する.
\r
499 * @return ディレクトリを監視する間隔(mSec)
\r
501 public int getDirWatchInterval() {
\r
502 return dirWatchInterval;
\r
505 public void setDirWatchInterval(int dirWatchInterval) {
\r
506 if (dirWatchInterval <= 0) {
\r
507 throw new IllegalArgumentException();
\r
509 this.dirWatchInterval = dirWatchInterval;
\r
512 private int dirWatchInterval = 7 * 1000;
\r
515 * ディレクトリの監視を有効にするか?
\r
516 * @return ディレクトリの監視を有効にする場合はtrue
\r
518 public boolean isEnableDirWatch() {
\r
519 return enableDirWatch;
\r
522 public void setEnableDirWatch(boolean enableDirWatch) {
\r
523 this.enableDirWatch = enableDirWatch;
\r
526 private boolean enableDirWatch = true;
\r
529 * ファイル転送に使うバッファサイズ.<br>
\r
532 public int getFileTransferBufferSize() {
\r
533 return fileTransferBufferSize;
\r
536 public void setFileTransferBufferSize(int fileTransferBufferSize) {
\r
537 if (fileTransferBufferSize <= 0) {
\r
538 throw new IllegalArgumentException();
\r
540 this.fileTransferBufferSize = fileTransferBufferSize;
\r
543 private int fileTransferBufferSize = 4096;
\r
546 * プレビューのインジケータを表示するまでのディレイ(mSec)を取得する.
\r
547 * @return プレビューのインジケータを表示するまでのディレイ(mSec)
\r
549 public long getPreviewIndicatorDelay() {
\r
550 return previewIndeicatorDelay;
\r
553 public void setPreviewIndeicatorDelay(long previewIndeicatorDelay) {
\r
554 if (previewIndeicatorDelay < 0) {
\r
555 throw new IllegalArgumentException();
\r
557 this.previewIndeicatorDelay = previewIndeicatorDelay;
\r
560 private long previewIndeicatorDelay = 300;
\r
563 * 情報ダイアログの編集ボタンを「開く」アクションにする場合はtrue、「編集」アクションにする場合はfalse
\r
564 * @return trueならばOpen、falseならばEdit
\r
566 public boolean isInformationDialogOpenMethod() {
\r
567 return informationDialogOpenMethod;
\r
570 public void setInformationDialogOpenMethod(
\r
571 boolean informationDialogOpenMethod) {
\r
572 this.informationDialogOpenMethod = informationDialogOpenMethod;
\r
575 private boolean informationDialogOpenMethod = true;
\r
579 * falseの場合は{@link ApplicationLogHandler}の実装に従って終了時に
\r
580 * 必要なければログは削除される.<br>
\r
581 * @return 常に残す場合はtrue、そうでなければfalse
\r
583 public boolean isNoRemoveLog() {
\r
584 return noRemoveLog;
\r
587 public void setNoRemoveLog(boolean noRemoveLog) {
\r
588 this.noRemoveLog = noRemoveLog;
\r
591 private boolean noRemoveLog = false;
\r
596 * @return テーブルのグリッド色
\r
598 public Color getGridColor() {
\r
602 public void setGridColor(Color gridColor) {
\r
603 if (gridColor == null) {
\r
604 throw new IllegalArgumentException();
\r
606 this.gridColor = gridColor;
\r
609 private Color gridColor = Color.gray;
\r
612 * カラーダイアログの値が変更されたら、自動的にプレビューを更新するか?
\r
613 * @return カラーダイアログの値が変更されたら、自動的にプレビューを更新する場合はtrue (デフォルトはtrue)
\r
615 public boolean isEnableAutoColorChange() {
\r
616 return enableAutoColorChange;
\r
619 public void setEnableAutoColorChange(boolean enableAutoColorChange) {
\r
620 this.enableAutoColorChange = enableAutoColorChange;
\r
623 private boolean enableAutoColorChange = true;
\r
625 public void setAuthorEditConflictBgColor(Color authorEditConflictBgColor) {
\r
626 if (authorEditConflictBgColor == null) {
\r
627 throw new IllegalArgumentException();
\r
629 this.authorEditConflictBgColor = authorEditConflictBgColor;
\r
633 * パーツの作者編集時に複数作者を選択した場合のに入力ボックスの背景色
\r
636 public Color getAuthorEditConflictBgColor() {
\r
637 return authorEditConflictBgColor;
\r
640 Color authorEditConflictBgColor = Color.yellow;
\r
643 public void setMainFrameMaxWidth(int width) {
\r
644 this.mainFrameMaxWidth = width;
\r
648 * メインフレームの初期表示時の最大幅
\r
649 * @return メインフレームの初期表示時の最大幅
\r
651 public int getMainFrameMaxWidth() {
\r
652 return mainFrameMaxWidth;
\r
655 private int mainFrameMaxWidth = 800;
\r
657 public void setMainFrameMaxHeight(int height) {
\r
658 this.mainFrameMaxHeight = height;
\r
662 * メインフレームの初期表示時の最大高さ
\r
663 * @return メインフレームの初期表示時の最大高さ
\r
665 public int getMainFrameMaxHeight() {
\r
666 return mainFrameMaxHeight;
\r
669 private int mainFrameMaxHeight = 600;
\r
673 * カラーダイアログで存在しないレイヤーをディセーブルにしない.
\r
674 * @return ディセーブルにしない場合はtrue
\r
676 public boolean isNotDisableLayerTab() {
\r
677 return notDisableLayerTab;
\r
680 public void setNotDisableLayerTab(boolean notDisableLayerTab) {
\r
681 this.notDisableLayerTab = notDisableLayerTab;
\r
684 private boolean notDisableLayerTab;
\r
689 * この指定日を経過した古いログは削除される.<br>
\r
693 public long getPurgeLogDays() {
\r
694 return purgeLogDays;
\r
697 public void setPurgeLogDays(long purgeLogDays) {
\r
698 this.purgeLogDays = purgeLogDays;
\r
701 private long purgeLogDays = 10;
\r
703 public String getPartsColorGroupPattern() {
\r
704 return partsColorGroupPattern;
\r
707 public void setPartsColorGroupPattern(String pattern) {
\r
708 if (pattern != null && pattern.trim().length() > 0) {
\r
709 Pattern.compile(pattern);
\r
711 partsColorGroupPattern = pattern;
\r
714 private String partsColorGroupPattern = "^.*\\(@\\).*$";
\r