From 94a8777d1e021eed608367951d9387309ab3e299 Mon Sep 17 00:00:00 2001 From: seraphy Date: Sun, 6 Oct 2013 17:57:42 +0000 Subject: [PATCH] =?utf8?q?=E3=83=BB=20=E3=82=AD=E3=83=A3=E3=83=A9=E3=82=AF?= =?utf8?q?=E3=82=BF=E3=83=BC=E3=83=87=E3=83=BC=E3=82=BF=E3=83=87=E3=82=A3?= =?utf8?q?=E3=83=AC=E3=82=AF=E3=83=88=E3=83=AA=E3=81=AE=E8=AA=AD=E3=81=BF?= =?utf8?q?=E8=BE=BC=E3=81=BF=E3=82=92=E4=B8=A6=E5=88=97=E5=8C=96=E3=81=97?= =?utf8?q?=E3=81=9F=E3=80=82=20=E3=83=BB=20=E3=82=AD=E3=83=A3=E3=83=A9?= =?utf8?q?=E3=82=AF=E3=82=BF=E3=83=BC=E3=83=87=E3=83=BC=E3=82=BF=E3=81=AE?= =?utf8?q?=E8=AA=AD=E3=81=BF=E8=BE=BC=E3=81=BF=E9=83=A8=E3=82=92=E3=82=AF?= =?utf8?q?=E3=83=A9=E3=82=B9=E3=81=AB=E5=88=86=E9=9B=A2=E3=81=97=E3=81=9F?= =?utf8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit git-svn-id: https://svn.sourceforge.jp/svnroot/charactermanaj/trunk@53 5b6e9025-a2e8-4882-b233-f889982098c5 --- .../model/io/AbstractCharacterDataArchiveFile.java | 5 +- .../model/io/CharacterDataDefaultProvider.java | 162 ++++++ .../model/io/CharacterDataIniReader.java | 177 ++++++ .../model/io/CharacterDataPersistent.java | 627 +++++++-------------- .../model/io/CharacterDataXMLReader.java | 3 - .../model/util/MakeEmbeddedResource.java | 3 +- src/charactermanaj/ui/ProfileEditDialog.java | 105 +++- src/charactermanaj/ui/ProfileListManager.java | 150 +++-- src/charactermanaj/ui/ProfileSelectorDialog.java | 7 +- src/charactermanaj/util/UserDataFactory.java | 85 ++- 10 files changed, 779 insertions(+), 545 deletions(-) create mode 100644 src/charactermanaj/model/io/CharacterDataDefaultProvider.java create mode 100644 src/charactermanaj/model/io/CharacterDataIniReader.java diff --git a/src/charactermanaj/model/io/AbstractCharacterDataArchiveFile.java b/src/charactermanaj/model/io/AbstractCharacterDataArchiveFile.java index bb81a2c..5e53a13 100644 --- a/src/charactermanaj/model/io/AbstractCharacterDataArchiveFile.java +++ b/src/charactermanaj/model/io/AbstractCharacterDataArchiveFile.java @@ -412,12 +412,11 @@ public abstract class AbstractCharacterDataArchiveFile try { // デフォルトのキャラクター定義を構築する. - CharacterDataPersistent persist = CharacterDataPersistent - .getInstance(); CharacterData cd; InputStream is = characterFile.openStream(); try { - cd = persist.readCharacterDataFromIni(is); + CharacterDataIniReader iniReader = new CharacterDataIniReader(); + cd = iniReader.readCharacterDataFromIni(is); } finally { is.close(); } diff --git a/src/charactermanaj/model/io/CharacterDataDefaultProvider.java b/src/charactermanaj/model/io/CharacterDataDefaultProvider.java new file mode 100644 index 0000000..bde4c62 --- /dev/null +++ b/src/charactermanaj/model/io/CharacterDataDefaultProvider.java @@ -0,0 +1,162 @@ +package charactermanaj.model.io; + +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.net.URL; +import java.net.URLConnection; +import java.util.Locale; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import charactermanaj.model.CharacterData; + +public class CharacterDataDefaultProvider { + + /** + * リソースに格納されているデフォルトのキャラクター定義.
+ */ + public static final String DEFAULT_CHARACTER_XML = "/schema/character.xml"; + + /** + * リソースに格納されているデフォルトのキャラクター定義のシリアライズデータ.
+ */ + public static final String DEFAULT_CHARACTER_XML_SER = "/schema/character.xml.ser"; + + /** + * ロガー + */ + private static final Logger logger = Logger + .getLogger(CharacterDataDefaultProvider.class.getName()); + + /** + * デフォルトのキャラクターデータのキャッシュ.
+ * リソース内にあるため、一度読み込んだら変更されることはないのでキャッシュしておく.
+ */ + private CharacterData defaultCharacterData; + + /** + * デフォルトのキャラクター定義を生成して返す.
+ * 一度生成された場合はキャッシュされる.
+ * 生成されたキャラクター定義のdocBaseはnullであるため、docBaseをセットすること.
+ * + * @return キャラクター定義 + */ + public synchronized CharacterData createDefaultCharacterData() { + try { + if (defaultCharacterData == null) { + CharacterData cd; + try { + // 埋め込みリソースからデフォルトキャラクターデータを構築する. + cd = loadEmbeddedSerializedDefaultCharacterData(); + + } catch (Exception ex) { + // 失敗した場合はXMLでの読み込みを試行する. + logger.log( + Level.WARNING, + "can't de-serialize the embedded default character-data.", + ex); + cd = null; + } + + if (cd == null) { + // XMLリソースからデフォルトキャラクターデータを構築する. + cd = loadEmbeddedXMLDefaultCharacterData(); + } + + assert (cd != null); + defaultCharacterData = cd; + } + + // コピーを返す. + return defaultCharacterData.duplicateBasicInfo(); + + } catch (IOException ex) { + throw new RuntimeException( + "can not create the default profile from application's resource", + ex); + } + } + + protected URL getEmbeddedResourceURL(String schemaName) { + return this.getClass().getResource(schemaName); + } + + /** + * シリアライズされたデフォルトキャラクターデータを埋め込みリソースより取得する.
+ * (現在のロケールの言語に対応するデータを取得し、なければenで代替する.)
+ * リソースがないか、読み込めない場合はnullを返す.
+ * 都度、リソースをデシリアライズする.
+ * + * @return キャラクター定義、もしくはnull + */ + protected CharacterData loadEmbeddedSerializedDefaultCharacterData() + throws IOException, ClassNotFoundException { + URL defaultSerializedCharacter = getEmbeddedResourceURL(DEFAULT_CHARACTER_XML_SER); + URL defaultCharacter = getEmbeddedResourceURL(DEFAULT_CHARACTER_XML); + + // 埋め込みリソースのシリアライズされたデフォルトキャラクターデータを復元する. + if (defaultSerializedCharacter != null) { + URLConnection connSer = defaultSerializedCharacter.openConnection(); + URLConnection connXml = defaultCharacter.openConnection(); + if (connXml.getLastModified() <= connSer.getLastModified()) { + Object obj; + InputStream is = connSer.getInputStream(); + try { + ObjectInputStream ois = new ObjectInputStream(is); + try { + obj = ois.readObject(); + } finally { + ois.close(); + } + } finally { + is.close(); + } + @SuppressWarnings("unchecked") + Map cdMap = (Map) obj; + + Locale locale = Locale.getDefault(); + String lang = locale.getLanguage(); + + CharacterData cd = cdMap.get(lang); + if (cd == null) { + // 指定した言語が見つからなければenを代表とする. + cd = cdMap.get("en"); + if (cd == null && !cdMap.isEmpty()) { + // それも見つからなければ、どれか1つを採用する. + cd = cdMap.values().iterator().next(); + } + } + return cd; + } + } + // リソースがないか、リソースの読み込みに失敗した場合. + return null; + } + + /** + * XMLリソースファイルから、デフォルトキャラクターデータを生成して返す.
+ * (現在のロケールの言語に対応するデータを取得し、なければ最初の言語で代替する.)
+ * 生成されたキャラクター定義のdocBaseはnullであるため、使用する場合はdocBaseをセットすること.
+ * 都度、XMLファイルから読み込まれる.
+ * + * @return デフォルトキャラクターデータ + * @throws IOException + * 失敗 + */ + protected CharacterData loadEmbeddedXMLDefaultCharacterData() + throws IOException { + CharacterData cd; + URL defaultCharacter = getEmbeddedResourceURL(DEFAULT_CHARACTER_XML); + InputStream is = defaultCharacter.openStream(); + try { + CharacterDataXMLReader characterDataXmlReader = new CharacterDataXMLReader(); + cd = characterDataXmlReader.loadCharacterDataFromXML(is, null); + + } finally { + is.close(); + } + return cd; + } +} diff --git a/src/charactermanaj/model/io/CharacterDataIniReader.java b/src/charactermanaj/model/io/CharacterDataIniReader.java new file mode 100644 index 0000000..ade1eef --- /dev/null +++ b/src/charactermanaj/model/io/CharacterDataIniReader.java @@ -0,0 +1,177 @@ +package charactermanaj.model.io; + +import java.awt.Dimension; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import charactermanaj.model.CharacterData; +import charactermanaj.model.PartsCategory; +import charactermanaj.model.PartsIdentifier; +import charactermanaj.model.PartsSet; + +/** + * character.iniファイルを読み込むためのクラス. + * + * @author seraphy + */ +public class CharacterDataIniReader { + + /** + * ロガー + */ + private static final Logger logger = Logger + .getLogger(CharacterDataIniReader.class.getName()); + + /** + * character.iniファイルから、キャラクター定義を生成します.
+ * docBaseは設定されていないため、戻り値に設定する必要があります.
+ * + * @param is + * character.iniの入力ストリーム + * @return キャラクターデータ + * @throws IOException + * 読み取りに失敗した場合 + */ + public CharacterData readCharacterDataFromIni(InputStream is) + throws IOException { + if (is == null) { + throw new IllegalArgumentException(); + } + + CharacterDataDefaultProvider defProv = new CharacterDataDefaultProvider(); + CharacterData cd = defProv.createDefaultCharacterData(); + + // イメージサイズ + int siz_x = 0; + int siz_y = 0; + + // パーツセット + HashMap plainPartsSet = new HashMap(); + + try { + BufferedReader rd; + try { + rd = new BufferedReader(new InputStreamReader(is, "MS932")); // SJISで読み取る. + } catch (UnsupportedEncodingException ex) { + logger.log(Level.SEVERE, "SJIS encoded file cannot be read.", + ex); + rd = new BufferedReader(new InputStreamReader(is)); // システムデフォルトで読み込む + } + + try { + String line; + int sectionMode = 0; + while ((line = rd.readLine()) != null) { + line = line.trim(); + if (line.length() == 0) { + continue; + } + + if (line.startsWith("[")) { + // セクションの判定 + if (line.toLowerCase().equals("[size]")) { + // Sizeセクション + sectionMode = 1; + } else if (line.toLowerCase().equals("[parts]")) { + // Partsセクション + sectionMode = 2; + } else { + // それ以外のセクションなのでモードをリセット. + // 色情報のセクション「Color」は現在のところサポートしていない. + sectionMode = 0; + } + } else { + int eqpos = line.indexOf('='); + String key, val; + if (eqpos >= 0) { + // キーは小文字に揃える (大小を無視して比較できるようにするため) + key = line.substring(0, eqpos).toLowerCase().trim(); + val = line.substring(eqpos + 1); + } else { + key = line.toLowerCase().trim(); + val = ""; + } + + if (sectionMode == 1) { + // Sizeセクション + try { + if (key.equals("size_x")) { + siz_x = Integer.parseInt(val); + } else if (key.equals("size_y")) { + siz_y = Integer.parseInt(val); + } + } catch (RuntimeException ex) { + logger.log(Level.WARNING, + "character.ini invalid. key=" + key + + "/val=" + val, ex); + // 変換できないものは無視する. + } + } else if (sectionMode == 2) { + // Partsセクション + if (key.length() > 0) { + plainPartsSet.put(key, val); + } + } + } + } + } finally { + rd.close(); + } + + } catch (IOException ex) { + // エラーが発生したら、character.iniは無かったことにして続ける. + logger.log(Level.WARNING, "character.ini invalid.", ex); + return null; + + } finally { + try { + is.close(); + } catch (IOException ex) { + logger.log(Level.SEVERE, "can't close file.", ex); + } + } + + // イメージサイズの設定 + if (siz_x > 0 && siz_y > 0) { + cd.setImageSize(new Dimension(siz_x, siz_y)); + } + + // パーツセットを構築する. + boolean existsPartsetParts = false; + if (!plainPartsSet.isEmpty()) { + PartsSet partsSet = new PartsSet("default", "default", true); + for (Map.Entry entry : plainPartsSet.entrySet()) { + String categoryId = entry.getKey(); + String partsName = entry.getValue(); + + PartsCategory partsCategory = cd.getPartsCategory(categoryId); + if (partsCategory != null) { + PartsIdentifier partsIdentifier; + if (partsName == null || partsName.length() == 0) { + partsIdentifier = null; + } else { + partsIdentifier = new PartsIdentifier(partsCategory, + partsName, partsName); + existsPartsetParts = true; + } + partsSet.appendParts(partsCategory, partsIdentifier, null); + } + } + if (!partsSet.isEmpty() && existsPartsetParts) { + // パーツセットが空でなく、 + // なにかしらのパーツが登録されている場合のみパーツセットを登録する. + cd.addPartsSet(partsSet); + cd.setDefaultPartsSetId("default"); + } + } + + return cd; + } +} diff --git a/src/charactermanaj/model/io/CharacterDataPersistent.java b/src/charactermanaj/model/io/CharacterDataPersistent.java index f09ff52..fc3c8e3 100644 --- a/src/charactermanaj/model/io/CharacterDataPersistent.java +++ b/src/charactermanaj/model/io/CharacterDataPersistent.java @@ -1,9 +1,7 @@ package charactermanaj.model.io; import java.awt.Color; -import java.awt.Dimension; import java.awt.image.BufferedImage; -import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileFilter; @@ -11,14 +9,10 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.ObjectInputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; -import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URL; -import java.net.URLConnection; import java.nio.charset.Charset; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -30,8 +24,14 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Locale; -import java.util.Map; import java.util.NoSuchElementException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.Logger; @@ -74,10 +74,8 @@ import charactermanaj.model.CharacterData; import charactermanaj.model.Layer; import charactermanaj.model.PartsAuthorInfo; import charactermanaj.model.PartsCategory; -import charactermanaj.model.PartsIdentifier; import charactermanaj.model.PartsManageData; import charactermanaj.model.PartsManageData.PartsKey; -import charactermanaj.model.PartsSet; import charactermanaj.ui.MainFrame; import charactermanaj.util.DirectoryConfig; import charactermanaj.util.FileNameNormalizer; @@ -130,12 +128,6 @@ public class CharacterDataPersistent { public static final String NS_PARTSDEF = "http://charactermanaj.sourceforge.jp/schema/charactermanaj-partsdef"; /** - * デフォルトのキャラクターデータのキャッシュ.
- * リソース内にあるため、一度読み込んだら変更されることはないのでキャッシュしておく.
- */ - private CharacterData defaultCharacterData; - - /** * キャラクター定義XML用のスキーマ定義リソース名 */ private static final String CHARACTER_XML_SCHEMA = "/schema/character.xsd"; @@ -156,16 +148,6 @@ public class CharacterDataPersistent { private static final String PARTSSET_XML_SCHEMA_0_8 = "/schema/0.8/partsset.xsd"; /** - * リソースに格納されているデフォルトのキャラクター定義.
- */ - public static final String DEFAULT_CHARACTER_XML = "/schema/character.xml"; - - /** - * リソースに格納されているデフォルトのキャラクター定義のシリアライズデータ.
- */ - public static final String DEFAULT_CHARACTER_XML_SER = "/schema/character.xml.ser"; - - /** * XMLのデータ形式.
* SchemaのバリデージョンチェックとDOMの解析を始める前にSAXで流し込んで、 * 最初のエレメント名や使用している名前空間、バージョンを読み込んで、 スキーマをきりかえられるようにするためのもの. @@ -383,14 +365,6 @@ public class CharacterDataPersistent { // 保存する. saveCharacterDataToXML(characterData); - // // シリアライズ形式でも保存する - // try { - // saveCharacterDataToSer(characterData); - // - // } catch (Exception ex) { - // // シリアライズに失敗しても無視する. - // } - // ディレクトリを準備する preparePartsDir(characterData); } @@ -442,14 +416,6 @@ public class CharacterDataPersistent { // 保存する saveCharacterDataToXML(characterData); - // // シリアライズ形式でも保存する. - // try { - // saveCharacterDataToSer(characterData); - // - // } catch (Exception ex) { - // // シリアライズに失敗したも無視する. - // } - // ディレクトリを準備する preparePartsDir(characterData); } @@ -501,137 +467,214 @@ public class CharacterDataPersistent { } /** - * プロファイルを列挙する.
- * 読み取りに失敗した場合はエラーハンドラに通知されるが例外は返されない.
- * 一つも正常なプロファイルがない場合は空のリストが返される.
+ * キャラクターデータを読み込んだ場合に返されるコールバック + */ + public interface ListProfileCallback { + + /** + * キャラクターデータを読み込んだ場合.
+ * 戻り値がfalseの場合は読み込みを以降の読み込みを中断します.
+ * (ただし、すでに読み込み開始している分については中断されません.) + * + * @param characterData + * @return 継続する場合はtrue、中止する場合はfalse + */ + boolean receiveCharacterData(CharacterData characterData); + + /** + * キャラクターデータの読み込みに失敗した場合.
+ * 戻り値がfalseの場合は読み込みを以降の読み込みを中断します.
+ * (ただし、すでに読み込み開始している分については中断されません.) + * + * @param dir + * 読み込み対象ディレクトリ + * @param ex + * 例外の内容 + * @return 継続する場合はtrue、中止する場合はfalse + */ + boolean occureException(File dir, Exception ex); + } + + /** + * キャラクターデータを非同期に読み込む.
+ * 読み込み完了したものが随時、コールバックに渡される. * - * @param errorHandler - * エラーハンドラ、不要ならばnull - * @return プロファイルのリスト(表示名順)、もしくは空 + * @param callback + * @return すべての読み込みが完了したか判定し待機することのできるFuture */ - public List listProfiles(ProfileListErrorHandler errorHandler) { + public Future listProfileAsync(final ListProfileCallback callback) { + if (callback == null) { + throw new IllegalArgumentException(); + } + + // キャラクターデータが格納されている親ディレクトリのリスト DirectoryConfig dirConfig = DirectoryConfig.getInstance(); File[] baseDirs = {dirConfig.getCharactersDir(),}; // ファイル名をノーマライズする FileNameNormalizer normalizer = FileNameNormalizer.getDefault(); - ArrayList profiles = new ArrayList(); - for (File baseDir : baseDirs) { - if (baseDir == null || !baseDir.exists() || !baseDir.isDirectory()) { - continue; - } - for (File dir : baseDir.listFiles(new FileFilter() { - public boolean accept(File pathname) { - boolean accept = pathname.isDirectory() - && !pathname.getName().startsWith("."); - if (accept) { - File configFile = new File(pathname, CONFIG_FILE); - accept = configFile.exists() && configFile.canRead(); - } - return accept; + // キャンセルしたことを示すフラグ + final AtomicBoolean cancelled = new AtomicBoolean(false); + + // 有効な論理CPU(CORE)数のスレッドで同時実行させる + int numOfProcessors = Runtime.getRuntime().availableProcessors(); + final ExecutorService executorSrv = Executors + .newFixedThreadPool(numOfProcessors); + try { + // キャラクターデータ対象ディレクトリを列挙し、並列に解析する + for (File baseDir : baseDirs) { + if (baseDir == null || !baseDir.exists() + || !baseDir.isDirectory()) { + continue; } - })) { - String path = normalizer.normalize(dir.getPath()); - File normDir = new File(path); - - File characterDataXml = new File(normDir, CONFIG_FILE); - if (characterDataXml.exists()) { - try { - File docBaseFile = new File(normDir, CONFIG_FILE); - URI docBase = docBaseFile.toURI(); - CharacterData characterData = loadProfile(docBase); - profiles.add(characterData); - - } catch (Exception ex) { - if (errorHandler != null) { - errorHandler.occureException(normDir, ex); + for (File dir : baseDir.listFiles(new FileFilter() { + public boolean accept(File pathname) { + boolean accept = pathname.isDirectory() + && !pathname.getName().startsWith("."); + if (accept) { + File configFile = new File(pathname, CONFIG_FILE); + accept = configFile.exists() + && configFile.canRead(); } + return accept; } + })) { + String path = normalizer.normalize(dir.getPath()); + final File normDir = new File(path); + + executorSrv.submit(new Runnable() { + public void run() { + boolean terminate = false; + File characterDataXml = new File(normDir, + CONFIG_FILE); + if (characterDataXml.exists()) { + try { + File docBaseFile = new File(normDir, + CONFIG_FILE); + URI docBase = docBaseFile.toURI(); + CharacterData characterData = loadProfile(docBase); + terminate = !callback + .receiveCharacterData(characterData); + + } catch (Exception ex) { + terminate = !callback.occureException( + normDir, ex); + } + } + if (terminate) { + // 中止が指示されたらスレッドプールを終了する + logger.log(Level.FINE, + "shutdownNow listProfile"); + executorSrv.shutdownNow(); + cancelled.set(true); + } + } + }); } } + } finally { + // タスクの登録を受付終了し、現在のすべてのタスクが完了したらスレッドは終了する. + executorSrv.shutdown(); } - Collections.sort(profiles, CharacterData.SORT_DISPLAYNAME); + // タスクの終了を待機できる疑似フューチャーを作成する. + Future awaiter = new Future() { + public boolean cancel(boolean mayInterruptIfRunning) { + if (executorSrv.isTerminated()) { + // すでに停止完了済み + return false; + } + executorSrv.shutdownNow(); + cancelled.set(true); + return true; + } - return Collections.unmodifiableList(profiles); + public boolean isCancelled() { + return cancelled.get(); + } + + public boolean isDone() { + return executorSrv.isTerminated(); + } + + public Object get() throws InterruptedException, ExecutionException { + try { + return get(300, TimeUnit.SECONDS); + + } catch (TimeoutException ex) { + throw new ExecutionException(ex); + } + } + + public Object get(long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, + TimeoutException { + executorSrv.shutdown(); + if (!executorSrv.isTerminated()) { + executorSrv.awaitTermination(timeout, unit); + } + return null; + } + }; + + return awaiter; } /** - * // * character.xmlのキャッシュファイルの位置を取得する. // * // * @param docBase // * @return - * character.xmlのシリアライズデータを格納するUserData // + * プロファイルを列挙する.
+ * 読み取りに失敗した場合はエラーハンドラに通知されるが例外は返されない.
+ * 一つも正常なプロファイルがない場合は空のリストが返される.
+ * エラーハンドラの通知は非同期に行われる. + * + * @param errorHandler + * エラーハンドラ、不要ならばnull + * @return プロファイルのリスト(表示名順)、もしくは空 */ - // protected UserData getCharacterDataCacheUserFile(URI docBase) { - // if (docBase == null) { - // throw new IllegalArgumentException(); - // } - // - // String name = new File(docBase).getName(); - // - // UserDataFactory userDataFactory = UserDataFactory.getInstance(); - // return userDataFactory.getMangledNamedUserData(docBase, name + - // "-cache.ser"); - // } + public List listProfiles( + final ProfileListErrorHandler errorHandler) { + + final List profiles = new ArrayList(); + + Future awaiter = listProfileAsync(new ListProfileCallback() { + + public boolean receiveCharacterData(CharacterData characterData) { + synchronized (profiles) { + profiles.add(characterData); + } + return true; + } + + public boolean occureException(File dir, Exception ex) { + if (errorHandler != null) { + errorHandler.occureException(dir, ex); + } + return true; + } + }); + + // すべてのキャラクターデータが読み込まれるまで待機する. + try { + awaiter.get(); + + } catch (Exception ex) { + logger.log(Level.WARNING, "listProfile abort.", ex); + } + + Collections.sort(profiles, CharacterData.SORT_DISPLAYNAME); + + return Collections.unmodifiableList(profiles); + } public CharacterData loadProfile(URI docBase) throws IOException { if (docBase == null) { throw new IllegalArgumentException(); } - // // docBaseの最終更新日を取得する. - // long lastModified; - // URL docBaseURL = docBase.toURL(); - // URLConnection conn = docBaseURL.openConnection(); - // try { - // lastModified = conn.getLastModified(); - // } finally { - // // コネクションを閉じる. - // conn.getInputStream().close(); - // conn = null; - // } - - // // XML解析済みの中間ファイルがあれば、それをデシリアライズする. - // UserData serializedFile = getCharacterDataCacheUserFile(docBase); - // if (serializedFile.exists()) { - // // 更新日がxmlと等しいか、より新しい場合のみ有効とする. - // if (lastModified > 0 && serializedFile.lastModified() >= - // lastModified) { - // try { - // CharacterData deserializedCd = (CharacterData) serializedFile.load(); - // - // // DocBaseがデシリアライズ結果と一致しないかぎり、有効としない. - // URI deserializedDocBase = deserializedCd.getDocBase(); - // if (deserializedDocBase == null || - // !docBase.equals(deserializedDocBase)) { - // throw new IOException("docBase mismatch. actual=" + - // deserializedDocBase + "/expected=" + docBase); - // } - // - // // デシリアライズ結果を有効なキャラクターデータとして返す. - // return deserializedCd; - // - // } catch (Exception ex) { - // logger.log(Level.WARNING, "cached character.xml loading failed.: " + - // docBase, ex); - // // デシリアライズに失敗した場合は無視して継続 - // } - // } - // } - // XMLから読み取る CharacterData characterData = characterDataXmlReader .loadCharacterDataFromXML(docBase); - // // XMLの読み取り結果をシリアライズする. - // try { - // serializedFile.save(characterData); - // - // } catch (Exception ex) { - // logger.log(Level.WARNING, "cached character.xml creation failed.: " + - // docBase, ex); - // // シリアライズに失敗しても処理は継続する. - // } - return characterData; } @@ -687,126 +730,6 @@ public class CharacterDataPersistent { return null; } - /** - * デフォルトのキャラクター定義を生成して返す.
- * 一度生成された場合はキャッシュされる.
- * 生成されたキャラクター定義のdocBaseはnullであるため、docBaseをセットすること.
- * - * @return キャラクター定義 - */ - public synchronized CharacterData createDefaultCharacterData() { - try { - if (defaultCharacterData == null) { - CharacterData cd; - try { - // 埋め込みリソースからデフォルトキャラクターデータを構築する. - cd = loadEmbeddedSerializedDefaultCharacterData(); - - } catch (Exception ex) { - // 失敗した場合はXMLでの読み込みを試行する. - logger.log( - Level.WARNING, - "can't de-serialize the embedded default character-data.", - ex); - cd = null; - } - - if (cd == null) { - // XMLリソースからデフォルトキャラクターデータを構築する. - cd = loadEmbeddedXMLDefaultCharacterData(); - } - - assert (cd != null); - defaultCharacterData = cd; - } - - // コピーを返す. - return defaultCharacterData.duplicateBasicInfo(); - - } catch (IOException ex) { - throw new RuntimeException( - "can not create the default profile from application's resource", - ex); - } - } - - /** - * XMLリソースファイルから、デフォルトキャラクターデータを生成して返す.
- * (現在のロケールの言語に対応するデータを取得し、なければ最初の言語で代替する.)
- * 生成されたキャラクター定義のdocBaseはnullであるため、使用する場合はdocBaseをセットすること.
- * 都度、XMLファイルから読み込まれる.
- * - * @return デフォルトキャラクターデータ - * @throws IOException - * 失敗 - */ - public CharacterData loadEmbeddedXMLDefaultCharacterData() - throws IOException { - CharacterData cd; - URL defaultCharacter = getEmbeddedResourceURL(DEFAULT_CHARACTER_XML); - InputStream is = defaultCharacter.openStream(); - try { - cd = characterDataXmlReader.loadCharacterDataFromXML(is, null); - - } finally { - is.close(); - } - return cd; - } - - /** - * シリアライズされたデフォルトキャラクターデータを埋め込みリソースより取得する.
- * (現在のロケールの言語に対応するデータを取得し、なければenで代替する.)
- * リソースがないか、読み込めない場合はnullを返す.
- * 都度、リソースをデシリアライズする.
- * - * @return キャラクター定義、もしくはnull - */ - public CharacterData loadEmbeddedSerializedDefaultCharacterData() - throws IOException, ClassNotFoundException { - URL defaultSerializedCharacter = getEmbeddedResourceURL(DEFAULT_CHARACTER_XML_SER); - URL defaultCharacter = getEmbeddedResourceURL(DEFAULT_CHARACTER_XML); - - // 埋め込みリソースのシリアライズされたデフォルトキャラクターデータを復元する. - if (defaultSerializedCharacter != null) { - URLConnection connSer = defaultSerializedCharacter.openConnection(); - URLConnection connXml = defaultCharacter.openConnection(); - if (connXml.getLastModified() <= connSer.getLastModified()) { - Object obj; - InputStream is = connSer.getInputStream(); - try { - ObjectInputStream ois = new ObjectInputStream(is); - try { - obj = ois.readObject(); - } finally { - ois.close(); - } - } finally { - is.close(); - } - @SuppressWarnings("unchecked") - Map cdMap = (Map) obj; - - Locale locale = Locale.getDefault(); - String lang = locale.getLanguage(); - - CharacterData cd = cdMap.get(lang); - if (cd == null) { - // 指定した言語が見つからなければenを代表とする. - cd = cdMap.get("en"); - if (cd == null && !cdMap.isEmpty()) { - // それも見つからなければ、どれか1つを採用する. - cd = cdMap.values().iterator().next(); - } - } - return cd; - } - } - // リソースがないか、リソースの読み込みに失敗した場合. - return null; - } - - protected void saveCharacterDataToXML(CharacterData characterData) throws IOException { @@ -849,25 +772,6 @@ public class CharacterDataPersistent { } } - // protected void saveCharacterDataToSer(CharacterData characterData) throws - // IOException { - // URI docBase = characterData.getDocBase(); - // if (docBase == null) { - // throw new IllegalArgumentException("docBaseがありません." + characterData); - // } - // try { - // UserData serializedFile = getCharacterDataCacheUserFile(docBase); - // serializedFile.save(characterData); - // - // } catch (IOException ex) { - // logger.log(Level.WARNING, "cached character.xml creation failed.: " + - // docBase, ex); - // throw ex; - // } - // } - - - public void saveFavorites(CharacterData characterData) throws IOException { if (characterData == null) { throw new IllegalArgumentException(); @@ -1006,13 +910,6 @@ public class CharacterDataPersistent { return; } - // // character.xml キャッシュの削除 - // UserData serializedFile = getCharacterDataCacheUserFile(docBase); - // if (serializedFile.exists()) { - // logger.log(Level.INFO, "remove file: " + serializedFile); - // serializedFile.delete(); - // } - // favories.xmlの削除 if (forceRemove) { UserData[] favoritesDatas = new UserData[]{getFavoritesUserData(cd)}; @@ -1768,150 +1665,6 @@ public class CharacterDataPersistent { return partsManageData; } - /** - * character.iniファイルから、キャラクター定義を生成します.
- * docBaseは設定されていないため、戻り値に設定する必要があります.
- * - * @param is - * character.iniの入力ストリーム - * @return キャラクターデータ - * @throws IOException - * 読み取りに失敗した場合 - */ - public CharacterData readCharacterDataFromIni(InputStream is) - throws IOException { - if (is == null) { - throw new IllegalArgumentException(); - } - - CharacterData cd = createDefaultCharacterData(); - - // イメージサイズ - int siz_x = 0; - int siz_y = 0; - - // パーツセット - HashMap plainPartsSet = new HashMap(); - - try { - BufferedReader rd; - try { - rd = new BufferedReader(new InputStreamReader(is, "MS932")); // SJISで読み取る. - } catch (UnsupportedEncodingException ex) { - logger.log(Level.SEVERE, "SJIS encoded file cannot be read.", - ex); - rd = new BufferedReader(new InputStreamReader(is)); // システムデフォルトで読み込む - } - - try { - String line; - int sectionMode = 0; - while ((line = rd.readLine()) != null) { - line = line.trim(); - if (line.length() == 0) { - continue; - } - - if (line.startsWith("[")) { - // セクションの判定 - if (line.toLowerCase().equals("[size]")) { - // Sizeセクション - sectionMode = 1; - } else if (line.toLowerCase().equals("[parts]")) { - // Partsセクション - sectionMode = 2; - } else { - // それ以外のセクションなのでモードをリセット. - // 色情報のセクション「Color」は現在のところサポートしていない. - sectionMode = 0; - } - } else { - int eqpos = line.indexOf('='); - String key, val; - if (eqpos >= 0) { - // キーは小文字に揃える (大小を無視して比較できるようにするため) - key = line.substring(0, eqpos).toLowerCase().trim(); - val = line.substring(eqpos + 1); - } else { - key = line.toLowerCase().trim(); - val = ""; - } - - if (sectionMode == 1) { - // Sizeセクション - try { - if (key.equals("size_x")) { - siz_x = Integer.parseInt(val); - } else if (key.equals("size_y")) { - siz_y = Integer.parseInt(val); - } - } catch (RuntimeException ex) { - logger.log(Level.WARNING, - "character.ini invalid. key=" + key - + "/val=" + val, ex); - // 変換できないものは無視する. - } - } else if (sectionMode == 2) { - // Partsセクション - if (key.length() > 0) { - plainPartsSet.put(key, val); - } - } - } - } - } finally { - rd.close(); - } - - } catch (IOException ex) { - // エラーが発生したら、character.iniは無かったことにして続ける. - logger.log(Level.WARNING, "character.ini invalid.", ex); - return null; - - } finally { - try { - is.close(); - } catch (IOException ex) { - logger.log(Level.SEVERE, "can't close file.", ex); - } - } - - // イメージサイズの設定 - if (siz_x > 0 && siz_y > 0) { - cd.setImageSize(new Dimension(siz_x, siz_y)); - } - - // パーツセットを構築する. - boolean existsPartsetParts = false; - if (!plainPartsSet.isEmpty()) { - PartsSet partsSet = new PartsSet("default", "default", true); - for (Map.Entry entry : plainPartsSet.entrySet()) { - String categoryId = entry.getKey(); - String partsName = entry.getValue(); - - PartsCategory partsCategory = cd.getPartsCategory(categoryId); - if (partsCategory != null) { - PartsIdentifier partsIdentifier; - if (partsName == null || partsName.length() == 0) { - partsIdentifier = null; - } else { - partsIdentifier = new PartsIdentifier(partsCategory, - partsName, partsName); - existsPartsetParts = true; - } - partsSet.appendParts(partsCategory, partsIdentifier, null); - } - } - if (!partsSet.isEmpty() && existsPartsetParts) { - // パーツセットが空でなく、 - // なにかしらのパーツが登録されている場合のみパーツセットを登録する. - cd.addPartsSet(partsSet); - cd.setDefaultPartsSetId("default"); - } - } - - return cd; - } /** * character.iniを読み取り、character.xmlを生成します.
@@ -1936,7 +1689,8 @@ public class CharacterDataPersistent { FileInputStream is = new FileInputStream(characterIniFile); CharacterData characterData; try { - characterData = readCharacterDataFromIni(is); + CharacterDataIniReader iniReader = new CharacterDataIniReader(); + characterData = iniReader.readCharacterDataFromIni(is); } finally { is.close(); @@ -1957,10 +1711,6 @@ public class CharacterDataPersistent { outstm.close(); } - // // キャッシュの生成 - // UserData serializedFile = getCharacterDataCacheUserFile(docBase); - // serializedFile.save(characterData); - succeeded = true; } finally { @@ -1994,7 +1744,8 @@ public class CharacterDataPersistent { // 補填の必要なし return; } - CharacterData defaultCd = createDefaultCharacterData(); + CharacterDataDefaultProvider defProv = new CharacterDataDefaultProvider(); + CharacterData defaultCd = defProv.createDefaultCharacterData(); characterData.setRecommendationURLList(defaultCd .getRecommendationURLList()); } diff --git a/src/charactermanaj/model/io/CharacterDataXMLReader.java b/src/charactermanaj/model/io/CharacterDataXMLReader.java index ef769f0..dd1a586 100644 --- a/src/charactermanaj/model/io/CharacterDataXMLReader.java +++ b/src/charactermanaj/model/io/CharacterDataXMLReader.java @@ -67,10 +67,7 @@ public class CharacterDataXMLReader { CharacterData cd; InputStream is = docBaseURL.openStream(); try { - long st = System.currentTimeMillis(); cd = loadCharacterDataFromXML(is, docBase); - long en = System.currentTimeMillis(); - System.out.println("所要時間: " + (en - st)); } finally { is.close(); diff --git a/src/charactermanaj/model/util/MakeEmbeddedResource.java b/src/charactermanaj/model/util/MakeEmbeddedResource.java index 916e73d..a1df480 100644 --- a/src/charactermanaj/model/util/MakeEmbeddedResource.java +++ b/src/charactermanaj/model/util/MakeEmbeddedResource.java @@ -13,6 +13,7 @@ import java.util.HashMap; import java.util.Locale; import charactermanaj.model.CharacterData; +import charactermanaj.model.io.CharacterDataDefaultProvider; import charactermanaj.model.io.CharacterDataPersistent; import charactermanaj.model.io.CharacterDataXMLReader; @@ -134,7 +135,7 @@ public class MakeEmbeddedResource { throws Exception { // 埋め込みリソースからデフォルトキャラクターデータを構築する. CharacterData cd; - URL defaultCharacter = getEmbeddedResourceURL(CharacterDataPersistent.DEFAULT_CHARACTER_XML); + URL defaultCharacter = getEmbeddedResourceURL(CharacterDataDefaultProvider.DEFAULT_CHARACTER_XML); InputStream is = defaultCharacter.openStream(); try { CharacterDataXMLReader xmlReader = new CharacterDataXMLReader(); diff --git a/src/charactermanaj/ui/ProfileEditDialog.java b/src/charactermanaj/ui/ProfileEditDialog.java index 4613eba..1038202 100644 --- a/src/charactermanaj/ui/ProfileEditDialog.java +++ b/src/charactermanaj/ui/ProfileEditDialog.java @@ -72,6 +72,7 @@ import charactermanaj.model.PartsCategory; import charactermanaj.model.PartsIdentifier; import charactermanaj.model.PartsSet; import charactermanaj.model.RecommendationURL; +import charactermanaj.model.io.CharacterDataDefaultProvider; import charactermanaj.model.io.CharacterDataPersistent; import charactermanaj.ui.model.AbstractTableModelWithComboBoxModel; import charactermanaj.util.DesktopUtilities; @@ -209,8 +210,11 @@ public class ProfileEditDialog extends JDialog { /** * キャラクターデータの編集画面を構築する.
- * @param parent 親、もしくはnull - * @param original オリジナルのキャラクターデータ(変更されない) + * + * @param parent + * 親、もしくはnull + * @param original + * オリジナルのキャラクターデータ(変更されない) */ public ProfileEditDialog(JFrame parent, CharacterData original) { super(parent, true); @@ -219,8 +223,11 @@ public class ProfileEditDialog extends JDialog { /** * キャラクターデータの編集画面を構築する.
- * @param parent 親、もしくはnull - * @param original オリジナルのキャラクターデータ(変更されない) + * + * @param parent + * 親、もしくはnull + * @param original + * オリジナルのキャラクターデータ(変更されない) */ public ProfileEditDialog(JDialog parent, CharacterData original) { super(parent, true); @@ -229,7 +236,9 @@ public class ProfileEditDialog extends JDialog { /** * 編集ダイアログを初期化する. - * @param origianl 編集もとキャラクター定義 + * + * @param origianl + * 編集もとキャラクター定義 */ private void initDialog(Component parent, CharacterData original) { // 元情報 @@ -1100,7 +1109,9 @@ public class ProfileEditDialog extends JDialog { /** * CharacterDataから画面へ転記する. - * @param original オリジナル情報 + * + * @param original + * オリジナル情報 */ protected void loadCharacterData(CharacterData original) { if (original == null) { @@ -1169,8 +1180,8 @@ public class ProfileEditDialog extends JDialog { List recommendationURLList = original.getRecommendationURLList(); if (recommendationURLList == null) { // キャラクターデータのお勧めリンクがnull(古い形式)の場合は、デフォルトのお勧めリンクで代替する. - CharacterDataPersistent persist = CharacterDataPersistent.getInstance(); - CharacterData defaultCd = persist.createDefaultCharacterData(); + CharacterDataDefaultProvider defProv = new CharacterDataDefaultProvider(); + CharacterData defaultCd = defProv.createDefaultCharacterData(); recommendationURLList = defaultCd.getRecommendationURLList(); } if (recommendationURLList != null) { @@ -1234,6 +1245,7 @@ public class ProfileEditDialog extends JDialog { /** * 入力データが有効であるか判定する. + * * @return 有効であればtrue */ protected boolean isValidData(ValidationReport report) { @@ -1323,6 +1335,7 @@ public class ProfileEditDialog extends JDialog { /** * 画面の情報からキャラクターデータを構築して返します.
+ * * @return キャラクターデータ */ protected CharacterData createCharacterData() { @@ -1412,8 +1425,8 @@ public class ProfileEditDialog extends JDialog { recommendationURLList.add(recommendationURL); } } - CharacterDataPersistent persist = CharacterDataPersistent.getInstance(); - CharacterData defaultCd = persist.createDefaultCharacterData(); + CharacterDataDefaultProvider defProv = new CharacterDataDefaultProvider(); + CharacterData defaultCd = defProv.createDefaultCharacterData(); List defaultRecommendationURLList = defaultCd.getRecommendationURLList(); if (defaultRecommendationURLList != null && defaultRecommendationURLList.equals(recommendationURLList)) { // デフォルトのお勧めリストと内容が同じの場合は、明示的にリストを設定しない. @@ -1425,8 +1438,8 @@ public class ProfileEditDialog extends JDialog { } /** - * 結果を取得する. - * キャンセルされた場合はnullが返される.
+ * 結果を取得する. キャンセルされた場合はnullが返される.
+ * * @return キャラクターデータ、またはnull */ public CharacterData getResult() { @@ -1437,6 +1450,7 @@ public class ProfileEditDialog extends JDialog { /** * 編集用カラーグループ + * * @author seraphy */ class ColorGroupsTableRow { @@ -1536,6 +1550,7 @@ class ColorGroupsTableRow { /** * カラーグループのテーブル編集モデル + * * @author seraphy */ class ColorGroupsTableModel extends AbstractTableModelWithComboBoxModel { @@ -1615,7 +1630,8 @@ class ColorGroupsTableModel extends AbstractTableModelWithComboBoxModel + * カラーグループ用のコンボボックスモデルに対して最初のアイテムとしてN/Aを常に追加するモデルに変換するラップクラス.
+ * * @author seraphy */ class FirstItemInjectionComboBoxModelWrapper implements ComboBoxModel { @@ -1648,7 +1664,9 @@ class FirstItemInjectionComboBoxModelWrapper implements ComboBoxModel { } /** * 親コンボボックスモデルのインデックスを+1したイベントに変換する. - * @param e 元イベント + * + * @param e + * 元イベント * @return インデックス変換後のイベント */ protected ListDataEvent convertRowIndex(ListDataEvent e) { @@ -1746,10 +1764,15 @@ class CategoriesTableRow implements Comparable { /** * カテゴリを構築する.
- * @param categoryId カテゴリ識別名 - * @param localizedCategoryName カテゴリ表示名 - * @param multipleSelectable 複数選択可能? - * @param layers レイヤー情報の配列 + * + * @param categoryId + * カテゴリ識別名 + * @param localizedCategoryName + * カテゴリ表示名 + * @param multipleSelectable + * 複数選択可能? + * @param layers + * レイヤー情報の配列 */ public CategoriesTableRow(final int order, final String categoryId, String localizedCategoryName, boolean multipleSelectable, int visibleRows, Layer[] layers) { @@ -1825,6 +1848,7 @@ class CategoriesTableRow implements Comparable { /** * 定義順を取得する + * * @return 定義順 */ public int getOrder() { @@ -1833,7 +1857,9 @@ class CategoriesTableRow implements Comparable { /** * 定義順を設定する - * @param order 定義順 + * + * @param order + * 定義順 */ public void setOrder(int order) { this.order = order; @@ -1841,6 +1867,7 @@ class CategoriesTableRow implements Comparable { /** * 複数選択可能であるか? + * * @return 複数選択可能であるか? */ public boolean isMultipleSelectable() { @@ -1849,7 +1876,9 @@ class CategoriesTableRow implements Comparable { /** * 複数選択可能であるか設定する - * @param multipleSelectable 複数選択可能であればtrue + * + * @param multipleSelectable + * 複数選択可能であればtrue */ public void setMultipleSelectable(boolean multipleSelectable) { this.multipleSelectable = multipleSelectable; @@ -1857,6 +1886,7 @@ class CategoriesTableRow implements Comparable { /** * 表示行数を取得する. + * * @return 表示行数 */ public int getVisibleRows() { @@ -1865,7 +1895,9 @@ class CategoriesTableRow implements Comparable { /** * 表示行数を設定する - * @param visibleRows 表示行数 + * + * @param visibleRows + * 表示行数 */ public void setVisibleRows(int visibleRows) { this.visibleRows = visibleRows; @@ -1873,7 +1905,9 @@ class CategoriesTableRow implements Comparable { /** * このカテゴリに指定したレイヤーが含まれるか検証する. - * @param layer レイヤー + * + * @param layer + * レイヤー * @return 含まれる場合はtrue、含まれない場合はfalse */ public boolean hasLayer(Layer layer) { @@ -1889,6 +1923,7 @@ class CategoriesTableRow implements Comparable { /** * レイヤー情報 + * * @return レイヤー情報 */ public Collection getLayers() { @@ -1897,6 +1932,7 @@ class CategoriesTableRow implements Comparable { /** * レイヤー情報 + * * @param layers */ public void setLayers(Collection layers) { @@ -1909,7 +1945,9 @@ class CategoriesTableRow implements Comparable { /** * レイヤーを取得する.
* 該当するレイヤーがなければnull - * @param layerId レイヤー名 + * + * @param layerId + * レイヤー名 * @return レイヤーもしくはnull */ public Layer getLayer(String layerId) { @@ -1926,6 +1964,7 @@ class CategoriesTableRow implements Comparable { /** * カテゴリ識別名を取得する. + * * @return カテゴリ識別名 */ public String getCategoryId() { @@ -1934,6 +1973,7 @@ class CategoriesTableRow implements Comparable { /** * カテゴリ表示名を取得する. + * * @return カテゴリ表示名 */ public String getLocalizedCategoryName() { @@ -1942,7 +1982,9 @@ class CategoriesTableRow implements Comparable { /** * カテゴリ表示名を設定する. - * @param localizedCategoryName カテゴリ表示名 + * + * @param localizedCategoryName + * カテゴリ表示名 */ public void setLocalizedCategoryName(String localizedCategoryName) { if (localizedCategoryName == null || localizedCategoryName.trim().length() == 0) { @@ -1965,8 +2007,9 @@ class CategoriesTableRow implements Comparable { /** * カテゴリのテーブル編集モデル. + * * @author seraphy - * + * */ class CategoriesTableModel extends AbstractTableModelWithComboBoxModel { @@ -2142,6 +2185,7 @@ class CategoriesTableModel extends AbstractTableModelWithComboBoxModel { @@ -2411,6 +2455,7 @@ class LayersTableModel extends AbstractTableModelWithComboBoxModel { @@ -2679,7 +2726,7 @@ class PartssetsTableModel extends AbstractTableModelWithComboBoxModel { diff --git a/src/charactermanaj/ui/ProfileListManager.java b/src/charactermanaj/ui/ProfileListManager.java index f0eea4b..4696af8 100644 --- a/src/charactermanaj/ui/ProfileListManager.java +++ b/src/charactermanaj/ui/ProfileListManager.java @@ -1,10 +1,13 @@ package charactermanaj.ui; import java.awt.Cursor; +import java.io.File; import java.io.IOException; import java.net.URI; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.concurrent.Future; import java.util.logging.Level; import java.util.logging.Logger; @@ -13,7 +16,9 @@ import javax.swing.JFrame; import charactermanaj.model.CharacterData; import charactermanaj.model.PartsManageData; +import charactermanaj.model.io.CharacterDataDefaultProvider; import charactermanaj.model.io.CharacterDataPersistent; +import charactermanaj.model.io.CharacterDataPersistent.ListProfileCallback; import charactermanaj.model.io.PartsDataLoader; import charactermanaj.model.io.PartsDataLoaderFactory; import charactermanaj.model.io.PartsManageDataDecorateLoader; @@ -23,6 +28,7 @@ import charactermanaj.util.ErrorMessageHelper; /** * プロファイルの選択・管理を行うクラス. + * * @author seraphy */ public final class ProfileListManager { @@ -48,7 +54,9 @@ public final class ProfileListManager { * キャラクターデータが使用中であるか?
* キャラクターデータのDocBaseをもとに判断する.
* nullを指定した場合は常にfalseを返す.
- * @param characterData キャラクターデータ、またはnull + * + * @param characterData + * キャラクターデータ、またはnull * @return 使用中であればtrue */ public static boolean isUsingCharacterData(CharacterData characterData) { @@ -63,7 +71,9 @@ public final class ProfileListManager { /** * キャラクターデータが使用中であることを登録する。 - * @param characterData キャラクターデータ + * + * @param characterData + * キャラクターデータ */ public static void registerUsedCharacterData(CharacterData characterData) { if (characterData == null) { @@ -85,7 +95,9 @@ public final class ProfileListManager { /** * キャラクターデータの使用中であることを登録解除する。 - * @param characterData キャラクターデータ + * + * @param characterData + * キャラクターデータ */ public static void unregisterUsedCharacterData(CharacterData characterData) { if (characterData == null) { @@ -111,9 +123,12 @@ public final class ProfileListManager { /** * プロファイル選択ダイアログを表示し、選択されたプロファイルのメインフレームを作成して返す.
* プロファイルの選択をキャンセルした場合はnullを返す.
- * @param parent 親フレーム + * + * @param parent + * 親フレーム * @return メインフレーム、もしくはnull - * @throws IOException 読み込みに失敗した場合 + * @throws IOException + * 読み込みに失敗した場合 */ public static MainFrame openProfile(JFrame parent) throws IOException { // キャラクタープロファイルのリストをロード @@ -144,9 +159,12 @@ public final class ProfileListManager { /** * 指定したキャラクター定義で新しいメインフレームを作成して返す.
- * @param characterData キャラクターデータ + * + * @param characterData + * キャラクターデータ * @return 作成されたメインフレーム - * @throws IOException 例外 + * @throws IOException + * 例外 */ public static MainFrame openProfile(CharacterData characterData) throws IOException { if (characterData == null || !characterData.isValid()) { @@ -167,7 +185,8 @@ public final class ProfileListManager { } /** - * キャラクター定義編集用ダイアログを生成して返す. + * キャラクター定義編集用ダイアログを生成して返す. + * * @author seraphy */ private interface ProfileEditorDialogFactory { @@ -178,10 +197,14 @@ public final class ProfileListManager { /** * キャラクター定義を編集する.
- * @param parent 親ダイアログ、もしくはnull - * @param characterData キャラクター定義(参照のみ、変更されない.) + * + * @param parent + * 親ダイアログ、もしくはnull + * @param characterData + * キャラクター定義(参照のみ、変更されない.) * @return 編集されたキャラクター定義、もしくはキャンセルされた場合はnull - * @throws IOException 失敗 + * @throws IOException + * 失敗 */ public static CharacterData editProfile(final JDialog parent, CharacterData characterData) throws IOException { return internalEditProfile(characterData, new ProfileEditorDialogFactory() { @@ -193,10 +216,14 @@ public final class ProfileListManager { /** * キャラクター定義を編集する.
- * @param parent 親フレーム、もしくはnull - * @param characterData キャラクター定義(参照のみ、変更されない.) + * + * @param parent + * 親フレーム、もしくはnull + * @param characterData + * キャラクター定義(参照のみ、変更されない.) * @return 編集されたキャラクター定義、もしくはキャンセルされた場合はnull - * @throws IOException 失敗 + * @throws IOException + * 失敗 */ public static CharacterData editProfile(final JFrame parent, CharacterData characterData) throws IOException { return internalEditProfile(characterData, new ProfileEditorDialogFactory() { @@ -208,10 +235,14 @@ public final class ProfileListManager { /** * キャラクター定義を編集する.
- * @param characterData キャラクター定義(参照のみ、変更されない.) - * @param dialogFactory キャラクター定義編集ダイアログを生成するファクトリ + * + * @param characterData + * キャラクター定義(参照のみ、変更されない.) + * @param dialogFactory + * キャラクター定義編集ダイアログを生成するファクトリ * @return 編集されたキャラクター定義、もしくはキャンセルされた場合はnull - * @throws IOException 失敗 + * @throws IOException + * 失敗 */ private static CharacterData internalEditProfile(CharacterData characterData, ProfileEditorDialogFactory dialogFactory) throws IOException { if (characterData == null || !characterData.isValid()) { @@ -234,7 +265,7 @@ public final class ProfileListManager { ProfileEditDialog editDlg = dialogFactory.create(original); editDlg.setVisible(true); - // 編集結果を得る. + // 編集結果を得る. CharacterData newCd = editDlg.getResult(); if (newCd == null) { // キャンセルされた場合 @@ -252,8 +283,10 @@ public final class ProfileListManager { /** * 最後にしようしたプロファイル、それがなければデフォルトプロファイルを開いて、そのメインフレームを返す. - * @return メインフレーム - * @throws IOException 開けなかった場合 + * + * @return メインフレーム + * @throws IOException + * 開けなかった場合 */ public static MainFrame openDefaultProfile() throws IOException { @@ -264,9 +297,11 @@ public final class ProfileListManager { // 最後に使用したプロファイルのロードを試行する. try { characterData = loadRecent(); - // キャラクターデータを読み込む - loadCharacterData(characterData); - loadFavorites(characterData); + if (characterData != null) { + // キャラクターデータを読み込む + loadCharacterData(characterData); + loadFavorites(characterData); + } } catch (Exception ex) { ErrorMessageHelper.showErrorDialog(null, ex); @@ -276,27 +311,54 @@ public final class ProfileListManager { // 最後に使用したプロファイルの記録がないか、プロファイルのロードに失敗した場合は // プロファイル一覧から最初のプロファイルを選択する. if (characterData == null) { - List profiles = persistent.listProfiles(CharacterDataPersistent.DEFAULT_ERROR_HANDLER); - for (CharacterData tryCd : profiles) { - try { - characterData = tryCd; - // キャラクターデータを読み込む - loadCharacterData(tryCd); - loadFavorites(tryCd); + final ArrayList profiles = new ArrayList(); + Future future = persistent + .listProfileAsync(new ListProfileCallback() { + public boolean receiveCharacterData( + CharacterData characterData) { + System.out.println("notify: " + characterData); + try { + loadCharacterData(characterData); + loadFavorites(characterData); + synchronized (profiles) { + profiles.add(characterData); + } + // 読み込めたものが1つでもあれば、以降は不要なので打ち切り + return false; - } catch (Exception ex) { - logger.log(Level.SEVERE, "プロファイルのロードに失敗しました。" + tryCd, ex); - // プロファイルの読み込みに失敗した場合は次を試行する. - characterData = null; + } catch (Exception ex) { + logger.log(Level.SEVERE, "プロファイルのロードに失敗しました。" + + characterData, ex); + // プロファイルの読み込みに失敗した場合は次を試行する. + return true; + } + } + public boolean occureException(File baseDir, + Exception ex) { + CharacterDataPersistent.DEFAULT_ERROR_HANDLER + .occureException(baseDir, ex); + // エラーでも継続する + return true; + } + }); + try { + future.get(); + synchronized (profiles) { + if (!profiles.isEmpty()) { + characterData = profiles.get(0); + } } + } catch (Exception ex) { + logger.log(Level.SEVERE, "プロファイルのロードに失敗しました。" + ex, ex); } } - + // プロファイルが一個もなければ、デフォルトのプロファイルの生成を試行する. if (characterData == null) { logger.info("オープンできるプロファイルがないため、新規プロファイルを作成します。"); try { - characterData = persistent.createDefaultCharacterData(); + CharacterDataDefaultProvider defProv = new CharacterDataDefaultProvider(); + characterData = defProv.createDefaultCharacterData(); persistent.createProfile(characterData); } catch (IOException ex) { @@ -320,8 +382,10 @@ public final class ProfileListManager { /** * キャラクターデータに、パーツデータをロードする.
* お気に入りはロードされないので、必要ならば、このあとで{@link #loadFavorites(CharacterData)}を呼び出す.
+ * * @param characterData - * @throws IOException 開けなかった場合 + * @throws IOException + * 開けなかった場合 */ public static void loadCharacterData(final CharacterData characterData) throws IOException { if (characterData != null && characterData.isValid()) { @@ -348,8 +412,10 @@ public final class ProfileListManager { /** * キャラクターデータに、お気に入りをロードする.
+ * * @param characterData - * @throws IOException 開けなかった場合 + * @throws IOException + * 開けなかった場合 */ public static void loadFavorites(final CharacterData characterData) throws IOException { if (characterData != null && characterData.isValid()) { @@ -360,8 +426,10 @@ public final class ProfileListManager { /** * 最後に使用したキャラクターデータとして記録する. + * * @param characterData - * @throws IOException 保存できなった場合 + * @throws IOException + * 保存できなった場合 */ public static void saveRecent(CharacterData characterData) throws IOException { RecentDataPersistent recentPersist = RecentDataPersistent.getInstance(); @@ -370,8 +438,10 @@ public final class ProfileListManager { /** * 最後に使用したキャラクターデータを取得する. + * * @return キャラクターデータ。最後に使用したデータが存在しない場合はnull - * @throws IOException 読み込みに失敗した場合 + * @throws IOException + * 読み込みに失敗した場合 */ public static CharacterData loadRecent() throws IOException { RecentDataPersistent recentPersist = RecentDataPersistent.getInstance(); diff --git a/src/charactermanaj/ui/ProfileSelectorDialog.java b/src/charactermanaj/ui/ProfileSelectorDialog.java index 7cf40e7..bfe4aa5 100644 --- a/src/charactermanaj/ui/ProfileSelectorDialog.java +++ b/src/charactermanaj/ui/ProfileSelectorDialog.java @@ -69,6 +69,7 @@ import charactermanaj.graphics.io.ImageCachedLoader; import charactermanaj.graphics.io.LoadedImage; import charactermanaj.model.AppConfig; import charactermanaj.model.CharacterData; +import charactermanaj.model.io.CharacterDataDefaultProvider; import charactermanaj.model.io.CharacterDataPersistent; import charactermanaj.model.io.PartsImageDirectoryWatchAgent; import charactermanaj.model.io.PartsImageDirectoryWatchAgentFactory; @@ -211,10 +212,7 @@ public class ProfileSelectorDialog extends JDialog { characterList.addListSelectionListener(new ListSelectionListener() { public void valueChanged(ListSelectionEvent e) { if (!e.getValueIsAdjusting()) { - long st = System.currentTimeMillis(); updateUIState(); - long en = System.currentTimeMillis(); - System.out.println("★切り替え所要時間:" + (en - st)); } } }); @@ -710,7 +708,8 @@ public class ProfileSelectorDialog extends JDialog { if (makeDefault || cd == null) { // デフォルトのプロファイルを使用します. - cd = persist.createDefaultCharacterData(); + CharacterDataDefaultProvider defProv = new CharacterDataDefaultProvider(); + cd = defProv.createDefaultCharacterData(); } // 基本情報をコピーします。 diff --git a/src/charactermanaj/util/UserDataFactory.java b/src/charactermanaj/util/UserDataFactory.java index 305e789..fbd0211 100644 --- a/src/charactermanaj/util/UserDataFactory.java +++ b/src/charactermanaj/util/UserDataFactory.java @@ -16,6 +16,7 @@ import java.util.logging.Logger; /** * ユーザーデータの保存先を生成するファクトリ + * * @author seraphy */ public class UserDataFactory { @@ -42,6 +43,7 @@ public class UserDataFactory { /** * インスタンスを取得する. + * * @return インスタンス */ public static UserDataFactory getInstance() { @@ -59,7 +61,9 @@ public class UserDataFactory { * 拡張子を含むファイル名を指定し、そのファイルが保存されるべきユーザディレクトリを判定して返す.
* nullまたは空の場合、もしくは拡張子がない場合はユーザディレクトリのルートを返します.
* フォルダがなければ作成されます.
- * @param name ファイル名、もしくはnull + * + * @param name + * ファイル名、もしくはnull * @return ファィルの拡張子に対応したデータ保存先フォルダ */ public File getSpecialDataDir(String name) { @@ -90,7 +94,9 @@ public class UserDataFactory { /** * 指定した名前のユーザーデータ保存先を作成する. - * @param name ファイル名 + * + * @param name + * ファイル名 * @return 保存先 */ public UserData getUserData(String name) { @@ -104,8 +110,11 @@ public class UserDataFactory { * docBaseごとにのハッシュ値を文字列表現化したプレフィックスをもつユーザーデータ保存先を作成する.
* docBaseのURIの圧縮を目的としており、等しいdocBaseは等しいプレフィックスによるようにしている.(暗号化が目的ではない).
* ハッシュ値はmd5の5バイトで生成されるため、既存のものと衝突した場合は末尾に数値が付与される.
- * @param docBase URI、null可 - * @param name ファイル名 + * + * @param docBase + * URI、null可 + * @param name + * ファイル名 * @return 保存先 */ public UserData getMangledNamedUserData(URI docBase, String name) { @@ -121,11 +130,15 @@ public class UserDataFactory { /** * ハッシュ化文字列のデータベースファイルをオープンし、現在の登録をすべて読み込む.
* クローズされるまでデータベースはロックされた状態となる.
- * @param storeDir データベースファイルの格納フォルダ + * + * @param storeDir + * データベースファイルの格納フォルダ * @return ハッシュ化文字列データベース - * @throws IOException 失敗 + * @throws IOException + * 失敗 */ - protected FileMappedProperties openMetaFile(File storeDir) throws IOException { + protected FileMappedProperties openMetaFile(File storeDir) + throws IOException { File metaFile = new File(storeDir, META_FILE); FileMappedProperties mangledProps = new FileMappedProperties(metaFile); @@ -139,7 +152,7 @@ public class UserDataFactory { logger.log(Level.WARNING, "mangled database is broken." + mangledProps.getFile(), ex); } - + return mangledProps; } @@ -149,11 +162,13 @@ public class UserDataFactory { /** * DocBaseのURIを辞書に登録し、そのハッシュ化文字列を返す.
- * ハッシュの衝突を回避するための辞書ファイル「mangled_info.xml」を使用して、 - * 衝突した場合は末尾に連番をふることで補正を行う.
+ * ハッシュの衝突を回避するための辞書ファイル「mangled_info.xml」を使用して、 衝突した場合は末尾に連番をふることで補正を行う.
* すでに登録済みの同じuriの場合は同じハッシュ化文字列を返す.
- * @param docBase URI - * @param mangledProps 辞書ファイル + * + * @param docBase + * URI + * @param mangledProps + * 辞書ファイル * @return ハッシュ化文字列(衝突した場合は連番が振られる) */ protected String registerMangledName(URI docBase, FileMappedProperties mangledProps) { @@ -190,7 +205,8 @@ public class UserDataFactory { // 登録済みUUIDリストに追加する. if (sameMangledURIList.length() > 0) { - sameMangledURIList += " "; // 空白区切り (URIの文字列表現では空白は含まれないため問題なし) + sameMangledURIList += " "; // 空白区切り + // (URIの文字列表現では空白は含まれないため問題なし) } sameMangledURIList += uri; @@ -216,10 +232,12 @@ public class UserDataFactory { /** * DocBaseのURIを辞書に登録し、そのハッシュ化文字列を返す.
- * ハッシュの衝突を回避するための辞書ファイル「mangled_info.xml」を使用して、 - * 衝突した場合は末尾に連番をふることで補正を行う.
- * @param docBase URI - * @param storeDir 格納先ディレクトリ(格納先単位で辞書ファイルが作成される) + * ハッシュの衝突を回避するための辞書ファイル「mangled_info.xml」を使用して、 衝突した場合は末尾に連番をふることで補正を行う.
+ * + * @param docBase + * URI + * @param storeDir + * 格納先ディレクトリ(格納先単位で辞書ファイルが作成される) * @return ハッシュ化文字列(衝突した場合は連番が振られる) */ protected String registerMangledName(URI docBase, File storeDir) { @@ -231,7 +249,7 @@ public class UserDataFactory { try { // 名前を登録する. adjustedMangledName = registerMangledName(docBase, mangledProps); - + // 追加・変更されていたら保存する. if (mangledProps.isModified()) { mangledProps.save(); @@ -244,18 +262,23 @@ public class UserDataFactory { } catch (Exception ex) { logger.log( Level.WARNING, - "mangled database is broken." + ((mangledProps == null) ? storeDir - : mangledProps.getFile()), ex); + "mangled database is broken." + + ((mangledProps == null) ? storeDir : mangledProps + .getFile()), ex); } - + return adjustedMangledName; } /** * 登録されている、すべてのハッシュ化文字列に対するURIを取得する.
- * @param mangledNames ハッシュ化文字列のコレクション - * @param storeDir 格納先ディレクトリ(格納先単位で辞書ファイルが作成される) - * @param ope ハッシュ化文字列に対応するURIが発見された場合のオペレーション + * + * @param mangledNames + * ハッシュ化文字列のコレクション + * @param storeDir + * 格納先ディレクトリ(格納先単位で辞書ファイルが作成される) + * @param ope + * ハッシュ化文字列に対応するURIが発見された場合のオペレーション */ public Map getMangledNameMap(File storeDir) { if (storeDir == null) { @@ -327,9 +350,13 @@ public class UserDataFactory { * URIのコレクションを指定して、それぞれの登録済みのハッシュ化文字列をマップとして返す.
* 登録フラグがfalseの場合、まだ登録されていないものはnullとなる.
* そうでない場合は新規に登録され、その値が設定される.
- * @param uris URIのコレクション - * @param storeDir 格納先ディレクトリ(格納先単位で辞書ファイルが作成される) - * @param register 検索時に存在しなければ登録する場合はtrue + * + * @param uris + * URIのコレクション + * @param storeDir + * 格納先ディレクトリ(格納先単位で辞書ファイルが作成される) + * @param register + * 検索時に存在しなければ登録する場合はtrue * @return URIをキーとし、ハッシュ化文字列を値とするマップ。登録されていないURIはnullが値となる.
*/ public Map getMangledNameMap(Collection uris, File storeDir, boolean register) { @@ -383,7 +410,9 @@ public class UserDataFactory { /** * docBaseをハッシュ値化文字列にした、補正前の文字列を返す.
* docBaseがnullの場合は空文字とみなして変換する.
- * @param docBase URI、null可 + * + * @param docBase + * URI、null可 * @return ハッシュ値の文字列表現 */ private String getNoAdjustedMangledName(URI docBase) { -- 2.11.0