1 package charactermanaj.model.io;
\r
3 import java.awt.image.BufferedImage;
\r
4 import java.io.ByteArrayInputStream;
\r
5 import java.io.ByteArrayOutputStream;
\r
7 import java.io.IOException;
\r
8 import java.io.InputStream;
\r
9 import java.io.InputStreamReader;
\r
10 import java.net.URI;
\r
11 import java.net.URISyntaxException;
\r
12 import java.util.ArrayList;
\r
13 import java.util.Collection;
\r
14 import java.util.Collections;
\r
15 import java.util.HashMap;
\r
16 import java.util.HashSet;
\r
17 import java.util.Locale;
\r
18 import java.util.Map;
\r
19 import java.util.logging.Level;
\r
20 import java.util.logging.Logger;
\r
22 import javax.imageio.ImageIO;
\r
24 import charactermanaj.graphics.io.PNGFileImageHeader;
\r
25 import charactermanaj.graphics.io.PNGFileImageHeaderReader;
\r
26 import charactermanaj.model.CharacterData;
\r
27 import charactermanaj.model.Layer;
\r
28 import charactermanaj.model.PartsCategory;
\r
29 import charactermanaj.model.PartsManageData;
\r
30 import charactermanaj.model.io.CharacterDataPersistent.DocInfo;
\r
32 public abstract class AbstractCharacterDataArchiveFile implements CharacterDataArchiveFile {
\r
34 private static final Logger logger = Logger.getLogger(AbstractCharacterDataArchiveFile.class.getName());
\r
36 protected File archiveFile;
\r
38 protected String rootPrefix = "";
\r
40 public interface FileContent {
\r
42 String getEntryName();
\r
44 long lastModified();
\r
46 InputStream openStream() throws IOException;
\r
51 public String toString() {
\r
52 return "CharacterDataArchiveFile(file=" + archiveFile + ")";
\r
55 public static final class CategoryLayerPair {
\r
57 private PartsCategory partsCategory;
\r
59 private Layer layer;
\r
61 public CategoryLayerPair(PartsCategory partsCategory, Layer layer) {
\r
62 if (partsCategory == null || layer == null) {
\r
63 throw new IllegalArgumentException();
\r
65 this.partsCategory = partsCategory;
\r
70 public int hashCode() {
\r
71 return partsCategory.hashCode() ^ layer.hashCode();
\r
75 public boolean equals(Object obj) {
\r
79 if (obj != null && obj instanceof CategoryLayerPair) {
\r
80 CategoryLayerPair o = (CategoryLayerPair) obj;
\r
81 return partsCategory.equals(o.partsCategory) && layer.equals(o.layer);
\r
86 public Layer getLayer() {
\r
90 public PartsCategory getPartsCategory() {
\r
91 return partsCategory;
\r
95 public String toString() {
\r
96 return "(" + partsCategory + ":" + layer +")";
\r
100 public static final class PartsImageContent implements FileContent {
\r
102 private final FileContent fileContent;
\r
104 private final Collection<CategoryLayerPair> categoryLayerPairs;
\r
106 private final String dirName;
\r
108 private final String partsName;
\r
110 private final String fileName;
\r
112 private final PNGFileImageHeader pngFileImageHeader;
\r
116 * @param fileContent ファイルコンテンツ
\r
117 * @param categoryLayerPairs カテゴリとレイヤーのペア
\r
118 * @param partsName ファイル名(ファイル名のみ。拡張子を含まない。パーツ名の元として用いることを想定。)
\r
119 * @param pngFileImageHeader PNGファイルヘッダ
\r
121 protected PartsImageContent(FileContent fileContent,
\r
122 Collection<CategoryLayerPair> categoryLayerPairs,
\r
123 String fileName, String partsName, PNGFileImageHeader pngFileImageHeader) {
\r
124 if (fileContent == null || categoryLayerPairs == null
\r
125 || categoryLayerPairs.isEmpty() || fileName == null
\r
126 || partsName == null || pngFileImageHeader == null) {
\r
127 throw new IllegalArgumentException();
\r
129 this.fileContent = fileContent;
\r
130 this.categoryLayerPairs = Collections.unmodifiableCollection(categoryLayerPairs);
\r
131 this.fileName = fileName;
\r
132 this.partsName = partsName;
\r
133 this.pngFileImageHeader = pngFileImageHeader;
\r
135 CategoryLayerPair categoryLayerPair = categoryLayerPairs.iterator().next();
\r
136 dirName = categoryLayerPair.getLayer().getDir();
\r
140 public int hashCode() {
\r
141 return getEntryName().hashCode();
\r
145 public boolean equals(Object obj) {
\r
149 if (obj != null && obj instanceof PartsImageContent) {
\r
150 return getEntryName().equals(((PartsImageContent) obj).getEntryName());
\r
155 public Collection<CategoryLayerPair> getCategoryLayerPairs() {
\r
156 return categoryLayerPairs;
\r
159 public String getDirName() {
\r
163 public String getEntryName() {
\r
164 return fileContent.getEntryName();
\r
167 public String getFileName() {
\r
171 public String getPartsName() {
\r
175 public PNGFileImageHeader getPngFileImageHeader() {
\r
176 return pngFileImageHeader;
\r
179 public long lastModified() {
\r
180 return fileContent.lastModified();
\r
183 public InputStream openStream() throws IOException {
\r
184 return fileContent.openStream();
\r
188 public String toString() {
\r
189 return fileContent.getEntryName();
\r
193 protected HashMap<String, FileContent> entries = new HashMap<String, FileContent>();
\r
195 protected AbstractCharacterDataArchiveFile(File archiveFile) {
\r
196 if (archiveFile == null) {
\r
197 throw new IllegalArgumentException();
\r
199 this.archiveFile = archiveFile;
\r
202 public File getArchiveFile() {
\r
203 return this.archiveFile;
\r
206 protected void addEntry(FileContent fileContent) {
\r
207 if (fileContent == null) {
\r
208 throw new IllegalArgumentException();
\r
210 if (logger.isLoggable(Level.FINE)) {
\r
211 logger.log(Level.FINE, fileContent.getEntryName());
\r
213 entries.put(fileContent.getEntryName(), fileContent);
\r
217 * アーカイブファイルをベースとしたURIを返す.<br>
\r
218 * @param name コンテンツ名
\r
220 * @throws IOException URIを生成できない場合
\r
222 protected URI getContentURI(String name) throws IOException {
\r
224 URI baseURI = archiveFile.toURI();
\r
225 return new URI("jar:" + baseURI.toString() + "/!" + name);
\r
227 } catch (URISyntaxException ex) {
\r
228 IOException iex = new IOException(ex.getMessage());
\r
236 * @param name コンテンツ名
\r
237 * @return 存在すればtrue、存在しなければfalse
\r
239 public boolean hasContent(String name) {
\r
240 return entries.containsKey(name);
\r
244 * 指定したコンテンツを取得する.<br>
\r
245 * 存在しない場合はnullを返す.<br>
\r
246 * @param name コンテンツ名
\r
247 * @return 存在すればコンテンツ、存在しなければnull
\r
249 public FileContent getContent(String name) {
\r
250 return entries.get(name);
\r
253 public String getRootPrefix() {
\r
254 return this.rootPrefix;
\r
258 * アーカイブのルート上に単一のフォルダしかない場合、そのフォルダを真のルートとして設定する.<br>
\r
259 * 返されるルート名には末尾に「/」を含む.<br>
\r
260 * ルート上に複数のフォルダがあるかファイルが存在する場合は空文字を設定する.<br>
\r
262 protected void searchRootPrefix() {
\r
263 HashSet<String> dirs = new HashSet<String>();
\r
264 for (String name : entries.keySet()) {
\r
265 int pos = name.indexOf('/');
\r
267 // ルート上にファイルがあるので、ここがルート
\r
272 String dir = name.substring(0, pos + 1);
\r
276 if (dirs.size() == 1) {
\r
277 // ルート上に単一のフォルダしかないので、
\r
279 rootPrefix = dirs.iterator().next();
\r
282 // ルート上に複数のフォルダがあるので、ここがルート
\r
287 * 指定したディレクトリ(フルパス指定)のファイルのみを取り出す.<br>
\r
289 * ディレクトリに空文字またはnullまたは「/」を指定した場合はルートを意味する.<br>
\r
290 * @param dir ディレクトリ
\r
291 * @return ファイルへのフルパスのコレクション
\r
293 public Map<String, FileContent> getFiles(String dir) {
\r
297 if (dir.length() > 0 && !dir.endsWith("/")) {
\r
300 if (dir.equals("/")) {
\r
301 dir = ""; // アーカイブ内コンテンツのパスは先頭は「/」ではないため。
\r
304 HashMap<String, FileContent> files = new HashMap<String, FileContent>();
\r
306 int ep = dir.length();
\r
307 for (Map.Entry<String, FileContent> entry : entries.entrySet()) {
\r
308 String name = entry.getKey();
\r
309 FileContent fileContent = entry.getValue();
\r
310 if (name.startsWith(dir)) {
\r
311 String suffix = name.substring(ep);
\r
312 int sep = suffix.indexOf('/');
\r
314 files.put(name, fileContent);
\r
323 * キャラクター定義を読み込む.<br>
\r
324 * アーカイブに存在しなければnull
\r
325 * @return キャラクター定義、もしくはnull
\r
326 * @throws IOException 読み取りに失敗した場合
\r
328 public CharacterData readCharacterData() throws IOException {
\r
329 FileContent characterFile = entries.get(rootPrefix + CharacterDataPersistent.CONFIG_FILE);
\r
330 if (characterFile == null) {
\r
334 CharacterDataPersistent persist = CharacterDataPersistent.getInstance();
\r
336 // character.xmlとして妥当な文書であるか検査する.
\r
338 InputStream is = characterFile.openStream();
\r
340 docInfo = persist.readDocumentType(is);
\r
341 logger.log(Level.INFO, docInfo == null ? "not xml document" : docInfo.toString());
\r
345 if (docInfo == null || !"character".equals(docInfo.getFirstElementName())) {
\r
349 // character.xmlを読み込む
\r
351 is = characterFile.openStream();
\r
353 URI docBase = getContentURI(rootPrefix + CharacterDataPersistent.CONFIG_FILE);
\r
354 cd = persist.loadCharacterDataFromXML(is, docBase, docInfo);
\r
363 * キャラクター定義をINIファイルより読み取る.<br>
\r
364 * アーカイブのコンテンツルート上のcharacter.iniを探す.<br>
\r
365 * それがなければ、アーカイブ上のどこかにある/character.iniを探して、もっとも短い場所にある1つを採用する.<br>
\r
366 * character.iniが何処にも存在しなければnull.<br>
\r
367 * 「キャラクターなんとか機 v2.2」の設定ファイルを想定している.<br>
\r
368 * @return キャラクター定義、もしくはnull
\r
369 * @throws IOException 読み取りに失敗した場合
\r
371 public CharacterData readCharacterINI() throws IOException {
\r
372 FileContent characterFile = null;
\r
373 characterFile = entries.get(rootPrefix + CharacterDataPersistent.COMPATIBLE_CONFIG_NAME);
\r
374 if (characterFile == null) {
\r
375 // どこかにあるcharacter.iniを探す
\r
376 ArrayList<String> characterInis = new ArrayList<String>();
\r
377 for (Map.Entry<String, FileContent> entry : entries.entrySet()) {
\r
378 String entryName = entry.getKey();
\r
379 if (entryName.endsWith("/" + CharacterDataPersistent.COMPATIBLE_CONFIG_NAME)) {
\r
380 characterInis.add(entryName);
\r
384 Collections.sort(characterInis);
\r
385 if (characterInis.size() > 0) {
\r
386 characterFile = entries.get(characterInis.get(0));
\r
389 if (characterFile == null) {
\r
390 // character.iniがないので何もしない.
\r
394 // デフォルトのキャラクター定義を構築する.
\r
395 CharacterDataPersistent persist = CharacterDataPersistent.getInstance();
\r
397 InputStream is = characterFile.openStream();
\r
399 cd = persist.readCharacterDataFromIni(is);
\r
405 URI docBase = getContentURI(rootPrefix + CharacterDataPersistent.COMPATIBLE_CONFIG_NAME);
\r
406 cd.setDocBase(docBase);
\r
412 * お気に入りを読み込みキャラクター定義に追加する.<br>
\r
413 * アーカイブにお気に入り(favorites.xml)が存在しなければ何もしない.<br>
\r
414 * @param characterData キャラクター定義(お気に入りが追加される)
\r
415 * @throws IOException 読み取りに失敗した場合
\r
417 public void readFavorites(CharacterData characterData) throws IOException {
\r
418 if (characterData == null) {
\r
419 throw new IllegalArgumentException("characterDataにnullは指定できません。");
\r
421 FileContent favoritesXml = entries.get(rootPrefix + "favorites.xml");
\r
422 if (favoritesXml == null) {
\r
423 // favorites.xmlがなければ何もしない
\r
427 CharacterDataPersistent persist = CharacterDataPersistent.getInstance();
\r
429 // favorites.xmlとして妥当な文書であるか検査する.
\r
431 InputStream is = favoritesXml.openStream();
\r
433 docInfo = persist.readDocumentType(is);
\r
434 logger.log(Level.INFO, docInfo == null ? "not xml document" : docInfo.toString());
\r
438 if (docInfo == null || !"partssets".equals(docInfo.getFirstElementName())) {
\r
439 // favorites.xmlの文書でない場合は何もしない.
\r
443 // favorites.xmlを読み込む
\r
444 is = favoritesXml.openStream();
\r
446 persist.loadPartsSet(characterData, is, docInfo);
\r
448 } catch (Exception ex) {
\r
449 logger.log(Level.INFO, "favorites.xml load failed.", ex);
\r
457 * サンプルピクチャを読み込む.<br>
\r
458 * アーカイブに存在しなければnull.
\r
459 * @return サンプルピクチャ、もしくはnull
\r
460 * @throws IOException 読み取りに失敗した場合
\r
462 public BufferedImage readSamplePicture() throws IOException {
\r
463 FileContent samplePictureFile = entries.get(rootPrefix + "preview.png");
\r
464 if (samplePictureFile == null) {
\r
465 Map<String, FileContent> files = getFiles(rootPrefix);
\r
467 samplePictureFile = files.get("preview.jpg");
\r
468 if (samplePictureFile == null) {
\r
469 samplePictureFile = files.get("preview.jpeg");
\r
472 if (samplePictureFile == null) {
\r
473 for (Map.Entry<String, FileContent> entry : files.entrySet()) {
\r
474 String name = entry.getKey();
\r
475 if (name.endsWith(".jpg") || name.endsWith(".jpeg") || name.endsWith(".png")) {
\r
476 samplePictureFile = entry.getValue();
\r
482 if (samplePictureFile == null) {
\r
487 InputStream is = samplePictureFile.openStream();
\r
489 pic = ImageIO.read(is);
\r
498 * アーカイブにある「readme.txt」、もしくは「readme」というファイルをテキストファイルとして読み込む.<br>
\r
499 * readme.txtが優先される。ともに存在しない場合はnull.<br>
\r
500 * 改行コードはプラットフォーム固有の改行コードに変換して返される.<br>
\r
501 * @return テキスト、もしくはnull
\r
502 * @throws 読み込みに失敗した場合
\r
504 public String readReadMe() throws IOException {
\r
506 Locale locale = Locale.getDefault();
\r
507 String lang = locale.getLanguage();
\r
509 ArrayList<FileContent> candiates = new ArrayList<FileContent>();
\r
511 Map<String, FileContent> files = getFiles(rootPrefix);
\r
512 for (String findName : new String[] {
\r
513 "readme_" + lang + ".txt", "readme_" + lang, "readme.txt", "readme", null }) {
\r
514 for (Map.Entry<String, FileContent> entry : files.entrySet()) {
\r
515 String name = entry.getKey().toLowerCase();
\r
516 if (findName == null && name.endsWith(".txt")) {
\r
517 candiates.add(entry.getValue());
\r
520 if (name.equals(findName)) {
\r
521 candiates.add(entry.getValue());
\r
525 if (candiates.isEmpty()) {
\r
529 // みつかったファイルについて読み込みを優先順で試行する.
\r
530 for (FileContent file : candiates) {
\r
532 return readTextUTF16(file);
\r
534 } catch (Exception ex) {
\r
535 logger.log(Level.WARNING, "read file failed. :" + file, ex);
\r
540 // すべて失敗したか、そもそもファイルがみつからなかった。
\r
545 * ファイルをテキストとして読み取り、返す.<br>
\r
546 * UTF-16LE/BE/UTF-8についてはBOMにより判定する.<br>
\r
547 * BOMがない場合はUTF-16/8ともに判定できない.<br>
\r
548 * BOMがなければMS932もしくはEUC_JPであると仮定して読み込む.<br>
\r
549 * 改行コードはプラットフォーム固有の改行コードに変換して返される.<br>
\r
550 * @param name コンテンツ名
\r
551 * @return テキスト、コンテンツが存在しない場合はnull
\r
552 * @throws IOException 読み込みに失敗した場合
\r
554 public String readTextFile(String name) throws IOException {
\r
555 if (name == null) {
\r
556 throw new IllegalArgumentException();
\r
558 FileContent content = entries.get(name);
\r
559 if (content == null) {
\r
562 return readTextUTF16(content);
\r
566 * ファイルをテキストとして読み取り、返す.<br>
\r
567 * UTF-16LE/BE/UTF-8についてはBOMにより判定する.<br>
\r
568 * BOMがない場合はUTF-16/8ともに判定できない.<br>
\r
569 * BOMがなければMS932もしくはEUC_JPであると仮定して読み込む.<br>
\r
570 * @param content コンテンツ
\r
572 * @throws IOException 読み込みに失敗した場合
\r
574 public String readTextUTF16(FileContent content) throws IOException {
\r
575 if (content == null) {
\r
576 throw new IllegalArgumentException();
\r
580 ByteArrayOutputStream bos = new ByteArrayOutputStream();
\r
581 InputStream is = content.openStream();
\r
584 while ((ch = is.read()) != -1) {
\r
585 bos.write((byte) ch);
\r
590 byte[] buf = bos.toByteArray();
\r
593 if (buf.length >= 2) {
\r
594 // Windowsのメモ帳はUTF-16にBOMをつけるので、これで判定できる。
\r
595 // 本アプリケーションのエクスポート時もUTF-16LEのBOM付きで出力する。
\r
596 // 一般的なエディタはUTF-16BEにはBOMをつけないので、事前に判定することはできない。
\r
597 if ((buf[0] & 0xff) == 0xff && (buf[1] & 0xff) == 0xfe) {
\r
599 } else if ((buf[0] & 0xff) == 0xfe && (buf[1] & 0xff) == 0xff) {
\r
603 if (enc == null && buf.length >= 3) {
\r
604 if ((buf[0] & 0xff) == 0xef && (buf[1] & 0xff) == 0xbb && (buf[1] & 0xff) == 0xbf) {
\r
605 // Windowsのメモ帳などはUTF-8にBOMをつけるので、これで判定できる。
\r
606 // 一般的なエディタではUTF-8のBOMはつけないのでUTF-8であるかどうかを事前判定することはできない。
\r
611 // BOMがない場合はMS932かEUC_JPのいずれかであろう、と仮定する。
\r
612 enc = "JISAutoDetect"; // SJIS/EUC_JPの自動判定
\r
616 StringBuilder str = new StringBuilder();
\r
617 InputStreamReader rd = new InputStreamReader(new ByteArrayInputStream(buf), enc);
\r
620 while ((ch = rd.read()) != -1) {
\r
621 str.append((char) ch);
\r
627 // 改行コードをプラットフォーム固有のものに変換
\r
628 String line = str.toString();
\r
629 line = line.replace("\r\n", "\n");
\r
630 line = line.replace("\r", "\n");
\r
631 line = line.replace("\n", System.getProperty("line.separator"));
\r
637 * キャラクター定義のカテゴリと、そのレイヤー情報から、画像のディレクトリの一覧をアーカイブ上のディレクトリの一覧として返す.<br>
\r
638 * ディレクトリの末尾は常にスラ付きとなる.<br>
\r
639 * enabledRootPefixがtrueの場合、ディレクトリの先頭はアーカイブのコンテンツルートとなる.<br>
\r
640 * 同一のディレクトリに対して複数のレイヤー(複数カテゴリを含む)が参照している場合、それらを含めて返す.<br>
\r
641 * 参照されているディレクトリがない場合は返される結果には含まれない.<br>
\r
642 * @param characterData キャラクター定義
\r
643 * @param enabledRootPrefix ルートプレフィックス(アーカイブのコンテンツルート)を付与する場合
\r
644 * @return パーツで使用する画像のディレクトリとして認識されるディレクトリの一覧、キーはアーカイブのディレクトリ位置、値は参照する1つ以上のレイヤー
\r
646 protected Map<String, Collection<CategoryLayerPair>> getLayerDirs(CharacterData characterData, boolean enabledRootPrefix) {
\r
647 if (characterData == null) {
\r
648 return Collections.emptyMap();
\r
651 String rootPrefix = getRootPrefix();
\r
652 HashMap<String, Collection<CategoryLayerPair>> layerDirs = new HashMap<String, Collection<CategoryLayerPair>>();
\r
653 for (PartsCategory partsCategory : characterData.getPartsCategories()) {
\r
654 for (Layer layer : partsCategory.getLayers()) {
\r
655 String dir = layer.getDir();
\r
656 if (!dir.endsWith("/")) {
\r
657 dir += "/"; // スラ付きにする.
\r
659 if (enabledRootPrefix) {
\r
660 // アーカイブのルートコンテキストからのディレクトリ位置とする.
\r
661 dir = rootPrefix + dir;
\r
663 Collection<CategoryLayerPair> sameDirLayers = layerDirs.get(dir);
\r
664 if (sameDirLayers == null) {
\r
665 sameDirLayers = new ArrayList<CategoryLayerPair>();
\r
666 layerDirs.put(dir, sameDirLayers);
\r
668 sameDirLayers.add(new CategoryLayerPair(partsCategory, layer));
\r
675 * アーカイブに含まれるフォルダをもつpngファイルからパーツイメージを取得する.<br>
\r
677 * @param インポート先のキャラクターデータ
\r
678 * 、フォルダ名などを判別するため。nullの場合はディレクトリなしとみなす.<br>
\r
680 * 新規インポート用であるか?(新規でない場合は引数で指定したキャラクターセットと同じパーツは読み込まれない).
\r
681 * (アーカイブファイルからの読み込みでは無視される)
\r
682 * @return パーツイメージコンテンツのコレクション、なければ空
\r
684 public Collection<PartsImageContent> getPartsImageContents(CharacterData characterData, boolean newly) {
\r
685 // コンテンツルートからの絶対位置指定でパーツイメージを取得する.
\r
686 Collection<PartsImageContent> results = getPartsImageContentsStrict(characterData);
\r
687 if (results.isEmpty()) {
\r
688 // コンテンツルートからの絶対位置にパーツがない場合は、任意のディレクトリ位置からパーツイメージを推定する.
\r
689 results = getPartsImageContentsLazy(characterData);
\r
695 * コンテンツルートからの絶対位置のフォルダからpngファイルからパーツイメージを取得する.<br>
\r
696 * @param インポート先のキャラクターデータ、フォルダ名などを判別するため。nullの場合はディレクトリなしとみなす.<br>
\r
697 * @return パーツイメージコンテンツのコレクション、なければ空
\r
699 protected Collection<PartsImageContent> getPartsImageContentsStrict(CharacterData characterData) {
\r
700 final Map<String, Collection<CategoryLayerPair>> layerDirMap = getLayerDirs(characterData, true);
\r
702 CategoryLayerPairResolveStrategy strategy = new CategoryLayerPairResolveStrategy() {
\r
703 public Collection<CategoryLayerPair> resolveCategoryLayerPairs(String dir) {
\r
704 Collection<CategoryLayerPair> categoryLayerPairs = layerDirMap.get(dir);
\r
705 if (categoryLayerPairs == null || categoryLayerPairs.isEmpty()) {
\r
706 // ディレクトリ名に一致するものがないので、この画像は無視する
\r
709 return categoryLayerPairs;
\r
713 return getPartsImageContents(strategy);
\r
717 * アーカイブに含まれる任意のフォルダからpngファイルからパーツイメージを取得する.<br>
\r
718 * ディレクトリ名の大文字・小文字は区別されません.<br>
\r
719 * @param インポート先のキャラクターデータ、フォルダ名などを判別するため。nullの場合はディレクトリなしとみなす.<br>
\r
720 * @return パーツイメージコンテンツのコレクション、なければ空
\r
722 protected Collection<PartsImageContent> getPartsImageContentsLazy(CharacterData characterData) {
\r
723 final Map<String, Collection<CategoryLayerPair>> layerDirMap = getLayerDirs(characterData, false);
\r
725 CategoryLayerPairResolveStrategy strategy = new CategoryLayerPairResolveStrategy() {
\r
726 public Collection<CategoryLayerPair> resolveCategoryLayerPairs(String dir) {
\r
727 dir = (dir == null) ? "" : dir.toLowerCase();
\r
728 for (Map.Entry<String, Collection<CategoryLayerPair>> entry : layerDirMap.entrySet()) {
\r
729 String dirSuffix = entry.getKey().toLowerCase();
\r
730 Collection<CategoryLayerPair> categoryLayerPairs = entry.getValue();
\r
731 if (dir.endsWith(dirSuffix)) {
\r
732 return categoryLayerPairs;
\r
739 return getPartsImageContents(strategy);
\r
743 * ディレクトリ名からカテゴリとレイヤーを取得するためのインターフェイス.<br>
\r
746 protected interface CategoryLayerPairResolveStrategy {
\r
749 * ディレクトリを指定して、それに該当するカテゴリとレイヤーペアのコレクションを返します.<br>
\r
750 * 同一のディレクトリに対して複数のレイヤーが割り当てられている可能性があるためコレクションで返されます.<br>
\r
751 * 空のコレクションにはなりません.<br>
\r
752 * レイヤーとして認識されていないディレクトリの場合はnullを返します.<br>
\r
753 * @param dir ディレクトリ
\r
754 * @return カテゴリ・レイヤーのペアのコレクション、またはnull (空のコレクションにはならない。)
\r
756 Collection<CategoryLayerPair> resolveCategoryLayerPairs(String dir);
\r
761 * アーカイブに含まれるフォルダをもつpngファイルからパーツイメージを取得する。
\r
762 * @param strategy ディレクトリが売れ入れ可能であるか判断するストラテジー
\r
763 * @return パーツイメージコンテンツのコレクション、なければ空
\r
765 protected Collection<PartsImageContent> getPartsImageContents(CategoryLayerPairResolveStrategy strategy) {
\r
766 if (strategy == null) {
\r
767 throw new IllegalArgumentException();
\r
770 ArrayList<PartsImageContent> results = new ArrayList<PartsImageContent>();
\r
771 for (Map.Entry<String, FileContent> entry : entries.entrySet()) {
\r
772 String name = entry.getKey();
\r
773 FileContent fileContent = entry.getValue();
\r
775 String[] split = name.split("/");
\r
776 if (split.length < 2) {
\r
777 // 最低でもフォルダ下になければならない
\r
780 String lastName = split[split.length - 1];
\r
781 if (!lastName.toLowerCase().endsWith(".png")) {
\r
787 String dir = name.substring(0, name.length() - lastName.length());
\r
789 // ディレクトリ名から対応するレイヤーを取得します.
\r
790 Collection<CategoryLayerPair> categoryLayerPairs = strategy.resolveCategoryLayerPairs(dir);
\r
791 if (categoryLayerPairs == null) {
\r
792 // パーツイメージのディレクトリとして定義されていない場合は、この画像は無視される.
\r
796 // PNGファイルヘッダの取得と確認
\r
797 PNGFileImageHeader pngFileHeader = readPNGFileHeader(fileContent);
\r
798 if (pngFileHeader == null) {
\r
799 // PNGファイルとして不正なものは無視する.
\r
800 logger.log(Level.WARNING, "invalid png: " + name);
\r
806 int extpos = lastName.lastIndexOf('.');
\r
807 partsName = lastName.substring(0, extpos);
\r
809 PartsImageContent partsImageContent = new PartsImageContent(
\r
810 fileContent, categoryLayerPairs, lastName, partsName, pngFileHeader);
\r
812 results.add(partsImageContent);
\r
818 * PNGファイルとしてファイルを読み込みPNGヘッダ情報を返します.<br>
\r
819 * PNGでないか、ファイルの読み込みに失敗した場合はnullを返します.<br>
\r
820 * @param fileContent 画像ファイル
\r
821 * @return PNGヘッダ情報、またはnull
\r
823 protected PNGFileImageHeader readPNGFileHeader(FileContent fileContent) {
\r
824 PNGFileImageHeaderReader pngHeaderReader = PNGFileImageHeaderReader.getInstance();
\r
825 PNGFileImageHeader pngFileHeader = null;
\r
827 InputStream is = fileContent.openStream();
\r
829 pngFileHeader = pngHeaderReader.readHeader(is);
\r
833 } catch (IOException ex) {
\r
834 logger.log(Level.WARNING, "not png header. " + fileContent, ex);
\r
835 pngFileHeader = null;
\r
838 return pngFileHeader;
\r
842 * アーカイブに含まれるparts-info.xmlを読み込み返す.<br>
\r
843 * 存在しなければ空のインスタンスを返す.<br>
\r
845 * @throws IOException
\r
847 public PartsManageData getPartsManageData() throws IOException {
\r
848 FileContent content = entries.get(rootPrefix + "parts-info.xml");
\r
849 if (content == null) {
\r
850 return new PartsManageData();
\r
853 PartsManageData partsManageData;
\r
855 InputStream is = content.openStream();
\r
857 CharacterDataPersistent persist = CharacterDataPersistent.getInstance();
\r
858 partsManageData = persist.loadPartsManageData(is);
\r
863 return partsManageData;
\r