OSDN Git Service

改行コード指定
authorOlyutorskii <olyutorskii@users.osdn.me>
Wed, 11 May 2011 03:19:08 +0000 (12:19 +0900)
committerOlyutorskii <olyutorskii@users.osdn.me>
Wed, 11 May 2011 03:19:08 +0000 (12:19 +0900)
22 files changed:
.hgeol [new file with mode: 0644]
CHANGELOG.txt
LICENSE.txt
README.txt
src/main/java/jp/sourceforge/jindolf/archiver/AvatarData.java
src/main/java/jp/sourceforge/jindolf/archiver/Builder.java
src/main/java/jp/sourceforge/jindolf/archiver/EventData.java
src/main/java/jp/sourceforge/jindolf/archiver/FileArchive.java
src/main/java/jp/sourceforge/jindolf/archiver/Handler.java
src/main/java/jp/sourceforge/jindolf/archiver/HttpAccess.java
src/main/java/jp/sourceforge/jindolf/archiver/JinArchiver.java
src/main/java/jp/sourceforge/jindolf/archiver/MultiPlexer.java
src/main/java/jp/sourceforge/jindolf/archiver/PeriodData.java
src/main/java/jp/sourceforge/jindolf/archiver/PeriodResource.java
src/main/java/jp/sourceforge/jindolf/archiver/TalkData.java
src/main/java/jp/sourceforge/jindolf/archiver/TopicData.java
src/main/java/jp/sourceforge/jindolf/archiver/ValidateTask.java
src/main/java/jp/sourceforge/jindolf/archiver/VillageData.java
src/main/java/jp/sourceforge/jindolf/archiver/Win31j.java
src/main/java/jp/sourceforge/jindolf/archiver/XmlUtils.java
src/main/java/jp/sourceforge/jindolf/archiver/ZipUtils.java
src/main/java/jp/sourceforge/jindolf/archiver/package-info.java

diff --git a/.hgeol b/.hgeol
new file mode 100644 (file)
index 0000000..ca5f1c1
--- /dev/null
+++ b/.hgeol
@@ -0,0 +1,13 @@
+[patterns]
+
+**.txt = native
+
+**.java = native
+**.properties = LF
+
+**.xml = LF
+**.xsd = LF
+
+**.css = LF
+**.html = LF
+**.png = BIN
index 7600e01..7adf9ac 100644 (file)
@@ -1,18 +1,18 @@
-[UTF-8 Japanese]\r
-\r
-\r
-JinArchiver 変更履歴\r
-\r
-\r
-1.502.2 (2011-04-21)\r
-    ・110421版スキーマに対応。\r
-\r
-1.501.2 (2011-04-20)\r
-    ・G国に対応。vanish,checkout,shortMember,counting2,executionタグの追加。\r
-    ・JinParser 1.407.2版に対応。\r
-    ・SCMをSubversionからMercurialに移行\r
-\r
-1.401.2 (2009-10-26)\r
-    ・初回リリース。\r
-\r
---- EOF ---\r
+[UTF-8 Japanese]
+
+
+JinArchiver 変更履歴
+
+
+1.502.2 (2011-04-21)
+    ・110421版スキーマに対応。
+
+1.501.2 (2011-04-20)
+    ・G国に対応。vanish,checkout,shortMember,counting2,executionタグの追加。
+    ・JinParser 1.407.2版に対応。
+    ・SCMをSubversionからMercurialに移行
+
+1.401.2 (2009-10-26)
+    ・初回リリース。
+
+--- EOF ---
index 862757d..6b9dd4e 100644 (file)
@@ -1,33 +1,33 @@
-[UTF-8 Japanese]\r
-\r
-The MIT License\r
-\r
-\r
-Copyright(c) 2009 olyutorskii\r
-\r
-\r
-Permission is hereby granted, free of charge, to any person obtaining a copy\r
-of this software and associated documentation files (the "Software"), to deal\r
-in the Software without restriction, including without limitation the rights\r
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
-copies of the Software, and to permit persons to whom the Software is\r
-furnished to do so, subject to the following conditions:\r
-\r
-The above copyright notice and this permission notice shall be included in\r
-all copies or substantial portions of the Software.\r
-\r
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r
-THE SOFTWARE.\r
-\r
-\r
-JinArchiver作者自身からのコメント:\r
-\r
-  ※ 少なくともこのソフトウェアの実行、複製、配布、改造は自由です。\r
-  ※ 少なくともこのソフトウェアは無保証です。\r
-\r
---- EOF ---\r
+[UTF-8 Japanese]
+
+The MIT License
+
+
+Copyright(c) 2009 olyutorskii
+
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+
+JinArchiver作者自身からのコメント:
+
+  ※ 少なくともこのソフトウェアの実行、複製、配布、改造は自由です。
+  ※ 少なくともこのソフトウェアは無保証です。
+
+--- EOF ---
index e8ed8f5..18a1087 100644 (file)
@@ -1,97 +1,97 @@
-[UTF-8 Japanese]\r
-\r
-                               JinArchiver\r
-                                  README\r
-\r
-                                              Copyright(c) 2009 olyutorskii\r
-\r
-\r
-=== JinArchiverとは ===\r
-\r
-\r
- JinArchiverは、人狼BBSの過去ログを独自のXML形式でローカルディスクに\r
-保存するためのツールです。\r
-\r
-※ このアーカイブにはJindolfの実行バイナリは含まれていません。\r
-  Jindolfを動かしたい方は、jindolfで始まり拡張子が*.jarであるファイルを\r
-  別途入手してください。\r
-※ 人狼BBSのURLは [ http://homepage2.nifty.com/ninjinia/ ] まで\r
-※ 人狼BBSを主催するninjin氏は、JinArchiverの製作に一切関与していません。\r
-  JinArchiverに関する問い合わせををninjin氏へ投げかけないように!約束だよ!\r
-\r
-\r
-=== 使い方 ===\r
-\r
-例) ※ F国 1507村 のアーカイブをディレクトリ/tmpに作りたい場合。\r
-\r
-java -jar jinarchiver-X.X.X.jar -land wolff -vid 1507 -outdir /tmp\r
-\r
-オプション詳細は-helpオプションで確認してください。\r
-\r
-\r
-=== ソースコードに関して ===\r
-\r
- - JinArchiverはJava言語(JLS3)で記述されたプログラムです。\r
- - JinArchiverはJRE1.5に準拠したJava実行環境で利用できるように作られています。\r
-   原則として、JRE1.5に準拠した実行系であれば、プラットフォームを選びません。\r
-\r
-\r
-=== アーカイブ管理体制 ===\r
-\r
-  このアーカイブは、UTF-8による開発環境を前提として構成されています。\r
-  このアーカイブの原本となる開発資産は、\r
-      http://hg.sourceforge.jp/view/jindolf/JinArchiver/\r
-  を上位に持つMercurialリポジトリで管理されています。\r
-  アーカイブの代わりにMercurialを通じて開発資産にアクセスすることにより、\r
-  任意の文字コードに変換されたJavaソースファイルや各種リソースを\r
-  容易に入手することが可能です。\r
-\r
-\r
-=== 開発プロジェクト運営元 ===\r
-\r
-  http://sourceforge.jp/projects/jindolf/ まで。\r
-\r
-\r
-=== ディレクトリ内訳構成 ===\r
-\r
-./README.txt\r
-    あなたが今見てるこれ。\r
-\r
-./CHANGELOG.txt\r
-    変更履歴。\r
-\r
-./LICENSE.txt\r
-    ライセンスに関して。\r
-\r
-./src/\r
-    Javaのソースコード。XMLなどの各種リソース。\r
-\r
-./test/\r
-    JUnit 4.* 用のテストコード。\r
-\r
-./pom.xml\r
-    Maven2用プロジェクト構成定義ファイル。\r
-\r
-./build.xml\r
-    Ant用追加タスク。\r
-\r
-./src/main/java/\r
-    Javaのソースコード。\r
-\r
-./src/main/resources/\r
-    プロパティファイルなどの各種リソース。\r
-\r
-./src/test/java/\r
-    JUnit 4.* 用のユニットテストコード。\r
-\r
-./src/main/config/checks.xml\r
-    Checkstyle用configファイル。\r
-\r
-./src/main/config/pmdrules.xml\r
-    PMD用ルール定義ファイル。\r
-\r
-./src/main/assembly/descriptor.xml\r
-    ソースアーカイブ構成定義ファイル。\r
-\r
-\r
---- EOF ---\r
+[UTF-8 Japanese]
+
+                               JinArchiver
+                                  README
+
+                                              Copyright(c) 2009 olyutorskii
+
+
+=== JinArchiverとは ===
+
+
+ JinArchiverは、人狼BBSの過去ログを独自のXML形式でローカルディスクに
+保存するためのツールです。
+
+※ このアーカイブにはJindolfの実行バイナリは含まれていません。
+  Jindolfを動かしたい方は、jindolfで始まり拡張子が*.jarであるファイルを
+  別途入手してください。
+※ 人狼BBSのURLは [ http://homepage2.nifty.com/ninjinia/ ] まで
+※ 人狼BBSを主催するninjin氏は、JinArchiverの製作に一切関与していません。
+  JinArchiverに関する問い合わせををninjin氏へ投げかけないように!約束だよ!
+
+
+=== 使い方 ===
+
+例) ※ F国 1507村 のアーカイブをディレクトリ/tmpに作りたい場合。
+
+java -jar jinarchiver-X.X.X.jar -land wolff -vid 1507 -outdir /tmp
+
+オプション詳細は-helpオプションで確認してください。
+
+
+=== ソースコードに関して ===
+
+ - JinArchiverはJava言語(JLS3)で記述されたプログラムです。
+ - JinArchiverはJRE1.5に準拠したJava実行環境で利用できるように作られています。
+   原則として、JRE1.5に準拠した実行系であれば、プラットフォームを選びません。
+
+
+=== アーカイブ管理体制 ===
+
+  このアーカイブは、UTF-8による開発環境を前提として構成されています。
+  このアーカイブの原本となる開発資産は、
+      http://hg.sourceforge.jp/view/jindolf/JinArchiver/
+  を上位に持つMercurialリポジトリで管理されています。
+  アーカイブの代わりにMercurialを通じて開発資産にアクセスすることにより、
+  任意の文字コードに変換されたJavaソースファイルや各種リソースを
+  容易に入手することが可能です。
+
+
+=== 開発プロジェクト運営元 ===
+
+  http://sourceforge.jp/projects/jindolf/ まで。
+
+
+=== ディレクトリ内訳構成 ===
+
+./README.txt
+    あなたが今見てるこれ。
+
+./CHANGELOG.txt
+    変更履歴。
+
+./LICENSE.txt
+    ライセンスに関して。
+
+./src/
+    Javaのソースコード。XMLなどの各種リソース。
+
+./test/
+    JUnit 4.* 用のテストコード。
+
+./pom.xml
+    Maven2用プロジェクト構成定義ファイル。
+
+./build.xml
+    Ant用追加タスク。
+
+./src/main/java/
+    Javaのソースコード。
+
+./src/main/resources/
+    プロパティファイルなどの各種リソース。
+
+./src/test/java/
+    JUnit 4.* 用のユニットテストコード。
+
+./src/main/config/checks.xml
+    Checkstyle用configファイル。
+
+./src/main/config/pmdrules.xml
+    PMD用ルール定義ファイル。
+
+./src/main/assembly/descriptor.xml
+    ソースアーカイブ構成定義ファイル。
+
+
+--- EOF ---
index 02d143c..652d9d8 100644 (file)
-/*\r
- * avatar model\r
- *\r
- * License : The MIT License\r
- * Copyright(c) 2008 olyutorskii\r
- */\r
-\r
-package jp.sourceforge.jindolf.archiver;\r
-\r
-import java.io.IOException;\r
-import java.io.Writer;\r
-import java.util.List;\r
-import javax.xml.parsers.DocumentBuilder;\r
-import javax.xml.parsers.DocumentBuilderFactory;\r
-import javax.xml.parsers.ParserConfigurationException;\r
-import jp.sourceforge.jindolf.corelib.PreDefAvatar;\r
-import org.xml.sax.SAXException;\r
-\r
-/**\r
- * Avatarモデル。\r
- */\r
-public class AvatarData{\r
-\r
-    private static final List<PreDefAvatar> PREDEF_AVATAR_LIST;\r
-\r
-    static{\r
-        DocumentBuilderFactory factory =\r
-                DocumentBuilderFactory.newInstance();\r
-        try{\r
-            DocumentBuilder builder = factory.newDocumentBuilder();\r
-            PREDEF_AVATAR_LIST = PreDefAvatar.buildPreDefAvatarList(builder);\r
-        }catch(ParserConfigurationException e){\r
-            throw new ExceptionInInitializerError(e);\r
-        }catch(IOException e){\r
-            throw new ExceptionInInitializerError(e);\r
-        }catch(SAXException e){\r
-            throw new ExceptionInInitializerError(e);\r
-        }\r
-    }\r
-\r
-\r
-    private String fullName;\r
-    private String shortName;\r
-    private String avatarId;\r
-    private String faceIconUri;\r
-\r
-\r
-    /**\r
-     * コンストラクタ。\r
-     */\r
-    public AvatarData(){\r
-        super();\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * コンストラクタ。\r
-     * @param predefAvatar プリセット済みAvatar\r
-     */\r
-    public AvatarData(PreDefAvatar predefAvatar){\r
-        this();\r
-\r
-        this.fullName = predefAvatar.getFullName();\r
-        this.shortName = predefAvatar.getShortName();\r
-        this.avatarId = predefAvatar.getAvatarId();\r
-        this.faceIconUri = null;\r
-\r
-        return;\r
-    }\r
-\r
-\r
-    /**\r
-     * プリセット済みAvatarをフルネームを用いて取得する。\r
-     * @param seq フルネーム\r
-     * @return 見つかったプリセット済みAvatar。見つからなければnull。\r
-     */\r
-    public static PreDefAvatar getPreDefAvatar(CharSequence seq){\r
-        for(PreDefAvatar avatar : PREDEF_AVATAR_LIST){\r
-            String fullName = avatar.getFullName();\r
-            if(fullName.contentEquals(seq)){\r
-                return avatar;\r
-            }\r
-        }\r
-        return null;\r
-    }\r
-\r
-    /**\r
-     * フルネームを取得する。\r
-     * @return フルネーム\r
-     */\r
-    public String getFullName(){\r
-        return this.fullName;\r
-    }\r
-\r
-    /**\r
-     * フルネームを設定する。\r
-     * @param fullName フルネーム\r
-     */\r
-    public void setFullName(String fullName){\r
-        this.fullName = fullName;\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 短縮名を取得する。\r
-     * @return 短縮名\r
-     */\r
-    public String getShortName(){\r
-        return this.shortName;\r
-    }\r
-\r
-    /**\r
-     * 短縮名を設定する。\r
-     * @param shortName 短縮名\r
-     */\r
-    public void setShortName(String shortName){\r
-        this.shortName = shortName;\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * Avatar識別子を取得する。\r
-     * @return Avatar識別子\r
-     */\r
-    public String getAvatarId(){\r
-        return this.avatarId;\r
-    }\r
-\r
-    /**\r
-     * Avatar識別子を設定する。\r
-     * @param avatarId Avatar識別子\r
-     */\r
-    public void setAvatarId(String avatarId){\r
-        this.avatarId = avatarId;\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 顔アイコンURI文字列を取得する。\r
-     * @return 顔アイコンURI文字列\r
-     */\r
-    public String getFaceIconUri(){\r
-        return this.faceIconUri;\r
-    }\r
-\r
-    /**\r
-     * 顔アイコンURI文字列を設定する。\r
-     * @param faceIconUri 顔アイコンURI文字列\r
-     */\r
-    public void setFaceIconUri(String faceIconUri){\r
-        this.faceIconUri = faceIconUri;\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * avatar要素をXML出力する。\r
-     * @param writer 出力先\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public void dumpXml(Writer writer) throws IOException{\r
-        writer.append("<avatar\n");\r
-\r
-        XmlUtils.indent(writer, 1);\r
-        XmlUtils.attrOut(writer, "avatarId", this.avatarId);\r
-        writer.append('\n');\r
-\r
-        XmlUtils.indent(writer, 1);\r
-        XmlUtils.attrOut(writer, "fullName", this.fullName);\r
-\r
-        writer.append(' ');\r
-        XmlUtils.attrOut(writer, "shortName", this.shortName);\r
-        writer.append('\n');\r
-\r
-        if(this.faceIconUri != null){\r
-            XmlUtils.indent(writer, 1);\r
-            XmlUtils.attrOut(writer, "faceIconURI", this.faceIconUri);\r
-            writer.append('\n');\r
-            // F1014対策\r
-        }\r
-\r
-        writer.append("/>\n");\r
-        writer.flush();\r
-\r
-        return;\r
-    }\r
-\r
-}\r
+/*
+ * avatar model
+ *
+ * License : The MIT License
+ * Copyright(c) 2008 olyutorskii
+ */
+
+package jp.sourceforge.jindolf.archiver;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.List;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import jp.sourceforge.jindolf.corelib.PreDefAvatar;
+import org.xml.sax.SAXException;
+
+/**
+ * Avatarモデル。
+ */
+public class AvatarData{
+
+    private static final List<PreDefAvatar> PREDEF_AVATAR_LIST;
+
+    static{
+        DocumentBuilderFactory factory =
+                DocumentBuilderFactory.newInstance();
+        try{
+            DocumentBuilder builder = factory.newDocumentBuilder();
+            PREDEF_AVATAR_LIST = PreDefAvatar.buildPreDefAvatarList(builder);
+        }catch(ParserConfigurationException e){
+            throw new ExceptionInInitializerError(e);
+        }catch(IOException e){
+            throw new ExceptionInInitializerError(e);
+        }catch(SAXException e){
+            throw new ExceptionInInitializerError(e);
+        }
+    }
+
+
+    private String fullName;
+    private String shortName;
+    private String avatarId;
+    private String faceIconUri;
+
+
+    /**
+     * コンストラクタ。
+     */
+    public AvatarData(){
+        super();
+        return;
+    }
+
+    /**
+     * コンストラクタ。
+     * @param predefAvatar プリセット済みAvatar
+     */
+    public AvatarData(PreDefAvatar predefAvatar){
+        this();
+
+        this.fullName = predefAvatar.getFullName();
+        this.shortName = predefAvatar.getShortName();
+        this.avatarId = predefAvatar.getAvatarId();
+        this.faceIconUri = null;
+
+        return;
+    }
+
+
+    /**
+     * プリセット済みAvatarをフルネームを用いて取得する。
+     * @param seq フルネーム
+     * @return 見つかったプリセット済みAvatar。見つからなければnull。
+     */
+    public static PreDefAvatar getPreDefAvatar(CharSequence seq){
+        for(PreDefAvatar avatar : PREDEF_AVATAR_LIST){
+            String fullName = avatar.getFullName();
+            if(fullName.contentEquals(seq)){
+                return avatar;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * フルネームを取得する。
+     * @return フルネーム
+     */
+    public String getFullName(){
+        return this.fullName;
+    }
+
+    /**
+     * フルネームを設定する。
+     * @param fullName フルネーム
+     */
+    public void setFullName(String fullName){
+        this.fullName = fullName;
+        return;
+    }
+
+    /**
+     * 短縮名を取得する。
+     * @return 短縮名
+     */
+    public String getShortName(){
+        return this.shortName;
+    }
+
+    /**
+     * 短縮名を設定する。
+     * @param shortName 短縮名
+     */
+    public void setShortName(String shortName){
+        this.shortName = shortName;
+        return;
+    }
+
+    /**
+     * Avatar識別子を取得する。
+     * @return Avatar識別子
+     */
+    public String getAvatarId(){
+        return this.avatarId;
+    }
+
+    /**
+     * Avatar識別子を設定する。
+     * @param avatarId Avatar識別子
+     */
+    public void setAvatarId(String avatarId){
+        this.avatarId = avatarId;
+        return;
+    }
+
+    /**
+     * 顔アイコンURI文字列を取得する。
+     * @return 顔アイコンURI文字列
+     */
+    public String getFaceIconUri(){
+        return this.faceIconUri;
+    }
+
+    /**
+     * 顔アイコンURI文字列を設定する。
+     * @param faceIconUri 顔アイコンURI文字列
+     */
+    public void setFaceIconUri(String faceIconUri){
+        this.faceIconUri = faceIconUri;
+        return;
+    }
+
+    /**
+     * avatar要素をXML出力する。
+     * @param writer 出力先
+     * @throws IOException 出力エラー
+     */
+    public void dumpXml(Writer writer) throws IOException{
+        writer.append("<avatar\n");
+
+        XmlUtils.indent(writer, 1);
+        XmlUtils.attrOut(writer, "avatarId", this.avatarId);
+        writer.append('\n');
+
+        XmlUtils.indent(writer, 1);
+        XmlUtils.attrOut(writer, "fullName", this.fullName);
+
+        writer.append(' ');
+        XmlUtils.attrOut(writer, "shortName", this.shortName);
+        writer.append('\n');
+
+        if(this.faceIconUri != null){
+            XmlUtils.indent(writer, 1);
+            XmlUtils.attrOut(writer, "faceIconURI", this.faceIconUri);
+            writer.append('\n');
+            // F1014対策
+        }
+
+        writer.append("/>\n");
+        writer.flush();
+
+        return;
+    }
+
+}
index 4274600..b6629a6 100644 (file)
-/*\r
- * information builder from input\r
- *\r
- * License : The MIT License\r
- * Copyright(c) 2008 olyutorskii\r
- */\r
-\r
-package jp.sourceforge.jindolf.archiver;\r
-\r
-import java.io.IOException;\r
-import java.io.InputStream;\r
-import java.net.URL;\r
-import java.net.URLConnection;\r
-import java.nio.charset.Charset;\r
-import jp.sourceforge.jindolf.parser.ContentBuilder;\r
-import jp.sourceforge.jindolf.parser.ContentBuilderSJ;\r
-import jp.sourceforge.jindolf.parser.ContentBuilderUCS2;\r
-import jp.sourceforge.jindolf.parser.DecodeException;\r
-import jp.sourceforge.jindolf.parser.DecodedContent;\r
-import jp.sourceforge.jindolf.parser.HtmlParseException;\r
-import jp.sourceforge.jindolf.parser.HtmlParser;\r
-import jp.sourceforge.jindolf.parser.SjisDecoder;\r
-import jp.sourceforge.jindolf.parser.StreamDecoder;\r
-\r
-/**\r
- * 入力から内部構造を生成する。\r
- */\r
-public final class Builder{\r
-\r
-    private static final int BUF_SZ = 100 * 1024;\r
-\r
-\r
-    /**\r
-     * 隠れコンストラクタ。\r
-     */\r
-    private Builder(){\r
-        super();\r
-        return;\r
-    }\r
-\r
-\r
-    /**\r
-     * 入力ストリームをデコードする。\r
-     * @param charset 文字コード指定\r
-     * @param istream 入力ストリーム\r
-     * @return デコード結果\r
-     * @throws IOException 入力エラー\r
-     * @throws DecodeException デコードエラー\r
-     */\r
-    public static DecodedContent contentFromStream(Charset charset,\r
-                                                     InputStream istream)\r
-            throws IOException, DecodeException{\r
-        StreamDecoder decoder;\r
-        ContentBuilder builder;\r
-\r
-        if(charset.name().equalsIgnoreCase("Shift_JIS")){\r
-            decoder = new SjisDecoder();\r
-            builder = new ContentBuilderSJ(BUF_SZ);\r
-        }else if(charset.name().equalsIgnoreCase("UTF-8")){\r
-            decoder = new StreamDecoder(charset.newDecoder());\r
-            builder = new ContentBuilderUCS2(BUF_SZ);\r
-        }else{\r
-            assert false;\r
-            return null;\r
-        }\r
-\r
-        decoder.setDecodeHandler(builder);\r
-\r
-        decoder.decode(istream);\r
-\r
-        DecodedContent content = builder.getContent();\r
-\r
-        return content;\r
-    }\r
-\r
-    /**\r
-     * 村の各日々をロードしパースする。\r
-     * @param villageData 村情報\r
-     * @throws IOException 入力エラー\r
-     * @throws DecodeException デコードエラー\r
-     * @throws HtmlParseException パースエラー\r
-     */\r
-    public static void fillVillageData(VillageData villageData)\r
-            throws IOException, DecodeException, HtmlParseException {\r
-        HtmlParser parser = new HtmlParser();\r
-        Handler handler = new Handler();\r
-        parser.setBasicHandler   (handler);\r
-        parser.setTalkHandler    (handler);\r
-        parser.setSysEventHandler(handler);\r
-\r
-        handler.initVillageData(villageData);\r
-\r
-        Charset charset = villageData.getLandDef().getEncoding();\r
-\r
-        for(PeriodResource resource : villageData.getPeriodResourceList()){\r
-            handler.initPeriodResource(resource);\r
-            URL url;\r
-            url = resource.getResourceUrl();\r
-            if(url == null){\r
-                url = new URL(resource.getOrigUrlText());\r
-            }\r
-            URLConnection conn = url.openConnection();\r
-            InputStream istream = conn.getInputStream();\r
-            if(resource.getDownTimeMs() <= 0){\r
-                long downTimeMs = conn.getDate();\r
-                resource.setDownTimeMs(downTimeMs);\r
-            }\r
-            DecodedContent content = contentFromStream(charset, istream);\r
-            istream.close();\r
-            parser.parseAutomatic(content);\r
-        }\r
-\r
-        return;\r
-    }\r
-\r
-}\r
+/*
+ * information builder from input
+ *
+ * License : The MIT License
+ * Copyright(c) 2008 olyutorskii
+ */
+
+package jp.sourceforge.jindolf.archiver;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.nio.charset.Charset;
+import jp.sourceforge.jindolf.parser.ContentBuilder;
+import jp.sourceforge.jindolf.parser.ContentBuilderSJ;
+import jp.sourceforge.jindolf.parser.ContentBuilderUCS2;
+import jp.sourceforge.jindolf.parser.DecodeException;
+import jp.sourceforge.jindolf.parser.DecodedContent;
+import jp.sourceforge.jindolf.parser.HtmlParseException;
+import jp.sourceforge.jindolf.parser.HtmlParser;
+import jp.sourceforge.jindolf.parser.SjisDecoder;
+import jp.sourceforge.jindolf.parser.StreamDecoder;
+
+/**
+ * 入力から内部構造を生成する。
+ */
+public final class Builder{
+
+    private static final int BUF_SZ = 100 * 1024;
+
+
+    /**
+     * 隠れコンストラクタ。
+     */
+    private Builder(){
+        super();
+        return;
+    }
+
+
+    /**
+     * 入力ストリームをデコードする。
+     * @param charset 文字コード指定
+     * @param istream 入力ストリーム
+     * @return デコード結果
+     * @throws IOException 入力エラー
+     * @throws DecodeException デコードエラー
+     */
+    public static DecodedContent contentFromStream(Charset charset,
+                                                     InputStream istream)
+            throws IOException, DecodeException{
+        StreamDecoder decoder;
+        ContentBuilder builder;
+
+        if(charset.name().equalsIgnoreCase("Shift_JIS")){
+            decoder = new SjisDecoder();
+            builder = new ContentBuilderSJ(BUF_SZ);
+        }else if(charset.name().equalsIgnoreCase("UTF-8")){
+            decoder = new StreamDecoder(charset.newDecoder());
+            builder = new ContentBuilderUCS2(BUF_SZ);
+        }else{
+            assert false;
+            return null;
+        }
+
+        decoder.setDecodeHandler(builder);
+
+        decoder.decode(istream);
+
+        DecodedContent content = builder.getContent();
+
+        return content;
+    }
+
+    /**
+     * 村の各日々をロードしパースする。
+     * @param villageData 村情報
+     * @throws IOException 入力エラー
+     * @throws DecodeException デコードエラー
+     * @throws HtmlParseException パースエラー
+     */
+    public static void fillVillageData(VillageData villageData)
+            throws IOException, DecodeException, HtmlParseException {
+        HtmlParser parser = new HtmlParser();
+        Handler handler = new Handler();
+        parser.setBasicHandler   (handler);
+        parser.setTalkHandler    (handler);
+        parser.setSysEventHandler(handler);
+
+        handler.initVillageData(villageData);
+
+        Charset charset = villageData.getLandDef().getEncoding();
+
+        for(PeriodResource resource : villageData.getPeriodResourceList()){
+            handler.initPeriodResource(resource);
+            URL url;
+            url = resource.getResourceUrl();
+            if(url == null){
+                url = new URL(resource.getOrigUrlText());
+            }
+            URLConnection conn = url.openConnection();
+            InputStream istream = conn.getInputStream();
+            if(resource.getDownTimeMs() <= 0){
+                long downTimeMs = conn.getDate();
+                resource.setDownTimeMs(downTimeMs);
+            }
+            DecodedContent content = contentFromStream(charset, istream);
+            istream.close();
+            parser.parseAutomatic(content);
+        }
+
+        return;
+    }
+
+}
index 70fcfc6..43e4ead 100644 (file)
-/*\r
- * system event\r
- *\r
- * License : The MIT License\r
- * Copyright(c) 2008 olyutorskii\r
- */\r
-\r
-package jp.sourceforge.jindolf.archiver;\r
-\r
-import java.io.IOException;\r
-import java.io.Writer;\r
-import java.util.LinkedList;\r
-import java.util.List;\r
-import jp.sourceforge.jindolf.corelib.GameRole;\r
-import jp.sourceforge.jindolf.corelib.SysEventType;\r
-import jp.sourceforge.jindolf.parser.DecodedContent;\r
-\r
-/**\r
- * システムイベント モデル。\r
- */\r
-public class EventData extends TopicData{\r
-\r
-    private SysEventType eventType = null;\r
-\r
-    private final List<AvatarData> avatarList = new LinkedList<AvatarData>();\r
-    private final List<Integer> intList = new LinkedList<Integer>();\r
-    private final List<GameRole> roleList = new LinkedList<GameRole>();\r
-    private final List<DecodedContent> strList =\r
-            new LinkedList<DecodedContent>();\r
-\r
-\r
-    /**\r
-     * コンストラクタ。\r
-     * @param periodData 所属元Period\r
-     */\r
-    public EventData(PeriodData periodData){\r
-        super();\r
-        return;\r
-    }\r
-\r
-\r
-    /**\r
-     * イベント種別からXML要素名を取得する。\r
-     * @param type イベント種別\r
-     * @return 要素名\r
-     */\r
-    public static String getTagName(SysEventType type){\r
-        String tagName;\r
-\r
-        switch(type){\r
-        case STARTENTRY:   tagName = "startEntry";   break;\r
-        case ONSTAGE:      tagName = "onStage";      break;\r
-        case STARTMIRROR:  tagName = "startMirror";  break;\r
-        case OPENROLE:     tagName = "openRole";     break;\r
-        case MURDERED:     tagName = "murdered";     break;\r
-        case STARTASSAULT: tagName = "startAssault"; break;\r
-        case SURVIVOR:     tagName = "survivor";     break;\r
-        case COUNTING:     tagName = "counting";     break;\r
-        case NOMURDER:     tagName = "noMurder";     break;\r
-        case SUDDENDEATH:  tagName = "suddenDeath";  break;\r
-        case WINVILLAGE:   tagName = "winVillage";   break;\r
-        case WINWOLF:      tagName = "winWolf";      break;\r
-        case WINHAMSTER:   tagName = "winHamster";   break;\r
-        case PLAYERLIST:   tagName = "playerList";   break;\r
-        case PANIC:        tagName = "panic";        break;\r
-        case EXECUTION:    tagName = "execution";    break;\r
-        case VANISH:       tagName = "vanish";       break;\r
-        case CHECKOUT:     tagName = "checkout";     break;\r
-        case SHORTMEMBER:  tagName = "shortMember";  break;\r
-        case ASKENTRY:     tagName = "askEntry";     break;\r
-        case ASKCOMMIT:    tagName = "askCommit";    break;\r
-        case NOCOMMENT:    tagName = "noComment";    break;\r
-        case STAYEPILOGUE: tagName = "stayEpilogue"; break;\r
-        case GAMEOVER:     tagName = "gameOver";     break;\r
-        case GUARD:        tagName = "guard";        break;\r
-        case JUDGE:        tagName = "judge";        break;\r
-        case COUNTING2:    tagName = "counting2";    break;\r
-        case ASSAULT:      tagName = "assault";      break;\r
-        default: throw new IllegalArgumentException();\r
-        }\r
-\r
-        return tagName;\r
-    }\r
-\r
-    /**\r
-     * 役職からXMLシンボル名を取得する。\r
-     * @param role 役職\r
-     * @return XMLシンボル名\r
-     */\r
-    public static String getRoleAttrValue(GameRole role){\r
-        String roleName;\r
-\r
-        switch(role){\r
-        case INNOCENT: roleName = "innocent"; break;\r
-        case WOLF:     roleName = "wolf";     break;\r
-        case SEER:     roleName = "seer";     break;\r
-        case SHAMAN:   roleName = "shaman";   break;\r
-        case MADMAN:   roleName = "madman";   break;\r
-        case HUNTER:   roleName = "hunter";   break;\r
-        case FRATER:   roleName = "frater";   break;\r
-        case HAMSTER:  roleName = "hamster";  break;\r
-        default: throw new IllegalArgumentException();\r
-        }\r
-\r
-        return roleName;\r
-    }\r
-\r
-    /**\r
-     * avatarRef要素をXML出力する。\r
-     * @param writer 出力先\r
-     * @param avatar Avatar\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public static void dumpAvatarRef(Writer writer, AvatarData avatar)\r
-            throws IOException{\r
-        writer.append("<avatarRef");\r
-        writer.append(' ');\r
-        XmlUtils.attrOut(writer, "avatarId", avatar.getAvatarId());\r
-        writer.append(" />\n");\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * システムイベント種別を取得する。\r
-     * @return システムイベント種別\r
-     */\r
-    public SysEventType getEventType(){\r
-        return this.eventType;\r
-    }\r
-\r
-    /**\r
-     * システムイベント種別を設定する。\r
-     * @param eventType システムイベント種別\r
-     */\r
-    public void setEventType(SysEventType eventType){\r
-        this.eventType = eventType;\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * Avatar情報を追加する。\r
-     * @param avatarData Avatar情報\r
-     */\r
-    public void addAvatarData(AvatarData avatarData){\r
-        this.avatarList.add(avatarData);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 整数情報を追加する。\r
-     * @param intVal 整数情報\r
-     */\r
-    public void addInteger(int intVal){\r
-        this.intList.add(intVal);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 役職情報を追加する。\r
-     * @param role 役職情報\r
-     */\r
-    public void addGameRole(GameRole role){\r
-        this.roleList.add(role);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 文字列情報を追加する。\r
-     * @param seq 文字列情報\r
-     */\r
-    public void addDecodedContent(DecodedContent seq){\r
-        this.strList.add(seq);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * ONSTAGE属性値をXML出力する。\r
-     * @param writer 出力先\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public void dumpOnstageAttr(Writer writer) throws IOException{\r
-        int entryNo = this.intList.get(0);\r
-        AvatarData avatarData = this.avatarList.get(0);\r
-\r
-        writer.append(' ');\r
-        XmlUtils.attrOut(writer, "entryNo", Integer.toString(entryNo));\r
-        writer.append(' ');\r
-        XmlUtils.attrOut(writer, "avatarId", avatarData.getAvatarId());\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * avatarId属性値をXML出力する。\r
-     * @param writer 出力先\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public void dumpSingleAvatarAttr(Writer writer) throws IOException{\r
-        AvatarData avatarData = this.avatarList.get(0);\r
-\r
-        writer.append(' ');\r
-        XmlUtils.attrOut(writer, "avatarId", avatarData.getAvatarId());\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * COUNTINGのvictim属性値をXML出力する。\r
-     * @param writer 出力先\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public void dumpCountingAttr(Writer writer) throws IOException{\r
-        int total = this.avatarList.size();\r
-        if(total % 2 != 0){\r
-            AvatarData victim = this.avatarList.get(total - 1);\r
-            writer.append(' ');\r
-            XmlUtils.attrOut(writer, "victim", victim.getAvatarId());\r
-        }\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * EXECUTIONのvictim属性値をXML出力する。\r
-     * @param writer 出力先\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public void dumpExecutionAttr(Writer writer) throws IOException{\r
-        int totalAvatar = this.avatarList.size();\r
-        int totalVotes = this.intList.size();\r
-        if(totalAvatar != totalVotes){\r
-            AvatarData victim = this.avatarList.get(totalAvatar - 1);\r
-            writer.append(' ');\r
-            XmlUtils.attrOut(writer, "victim", victim.getAvatarId());\r
-        }\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * ASKENTRY属性値をXML出力する。\r
-     * @param writer 出力先\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public void dumpAskEntryAttr(Writer writer) throws IOException{\r
-        int hour     = this.intList.get(0);\r
-        int minute   = this.intList.get(1);\r
-        int minLimit = this.intList.get(2);\r
-        int maxLimit = this.intList.get(3);\r
-\r
-        writer.append(' ');\r
-        XmlUtils.timeAttrOut(writer, "commitTime", hour, minute);\r
-\r
-        writer.append(' ');\r
-        XmlUtils.attrOut(writer, "minMembers", Integer.toString(minLimit));\r
-\r
-        writer.append(' ');\r
-        XmlUtils.attrOut(writer, "maxMembers", Integer.toString(maxLimit));\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * ASKCOMMIT属性値をXML出力する。\r
-     * @param writer 出力先\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public void dumpAskCommitAttr(Writer writer) throws IOException{\r
-        int hour     = this.intList.get(0);\r
-        int minute   = this.intList.get(1);\r
-\r
-        writer.append(' ');\r
-        XmlUtils.timeAttrOut(writer, "limitVote", hour, minute);\r
-\r
-        writer.append(' ');\r
-        XmlUtils.timeAttrOut(writer, "limitSpecial", hour, minute);\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * STAYEPILOGUE属性値をXML出力する。\r
-     * @param writer 出力先\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public void dumpStayEpilogueAttr(Writer writer) throws IOException{\r
-        GameRole role = this.roleList.get(0);\r
-        int hour   = this.intList.get(0);\r
-        int minute = this.intList.get(1);\r
-\r
-        String winner;\r
-        switch(role){\r
-        case INNOCENT: winner = "village"; break;\r
-        case WOLF:     winner = "wolf";    break;\r
-        case HAMSTER:  winner = "hamster"; break;\r
-        default: throw new IllegalArgumentException();\r
-        }\r
-        writer.append(' ');\r
-        XmlUtils.attrOut(writer, "maxMembers", winner);\r
-\r
-        writer.append(' ');\r
-        XmlUtils.timeAttrOut(writer, "limitTime", hour, minute);\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * openRole子要素をXML出力する。\r
-     * @param writer 出力先\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public void dumpOpenroleElem(Writer writer) throws IOException{\r
-        int num = this.roleList.size();\r
-        for(int index = 0; index < num; index++){\r
-            int heads = this.intList.get(index);\r
-            GameRole role = this.roleList.get(index);\r
-            String roleName = getRoleAttrValue(role);\r
-\r
-            writer.append("<roleHeads");\r
-            writer.append(' ');\r
-            XmlUtils.attrOut(writer, "role", roleName);\r
-            writer.append(' ');\r
-            XmlUtils.attrOut(writer, "heads", Integer.toString(heads));\r
-            writer.append(" />\n");\r
-        }\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * murdered子要素をXML出力する。\r
-     * @param writer 出力先\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public void dumpMurderedElem(Writer writer) throws IOException{\r
-        for(AvatarData avatar : this.avatarList){\r
-            dumpAvatarRef(writer, avatar);\r
-        }\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * survivor子要素をXML出力する。\r
-     * @param writer 出力先\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public void dumpSurvivorElem(Writer writer) throws IOException{\r
-        for(AvatarData avatar : this.avatarList){\r
-            dumpAvatarRef(writer, avatar);\r
-        }\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * nocomment子要素をXML出力する。\r
-     * @param writer 出力先\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public void dumpNoCommentElem(Writer writer) throws IOException{\r
-        for(AvatarData avatar : this.avatarList){\r
-            dumpAvatarRef(writer, avatar);\r
-        }\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * counting子要素をXML出力する。\r
-     * @param writer 出力先\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public void dumpCountingElem(Writer writer) throws IOException{\r
-        int total = this.avatarList.size();\r
-        total = total / 2 * 2;\r
-        for(int index = 0; index < total; index += 2){\r
-            AvatarData voteBy = this.avatarList.get(index);\r
-            AvatarData voteTo = this.avatarList.get(index + 1);\r
-            writer.append("<vote");\r
-            writer.append(' ');\r
-            XmlUtils.attrOut(writer, "byWhom", voteBy.getAvatarId());\r
-            writer.append(' ');\r
-            XmlUtils.attrOut(writer, "target", voteTo.getAvatarId());\r
-            writer.append(" />\n");\r
-        }\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * execution子要素をXML出力する。\r
-     * @param writer 出力先\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public void dumpExecutionElem(Writer writer) throws IOException{\r
-        int total = this.intList.size();\r
-        for(int index = 0; index < total; index++){\r
-            AvatarData voteTo = this.avatarList.get(index);\r
-            int count = this.intList.get(index);\r
-            writer.append("<nominated");\r
-            writer.append(' ');\r
-            XmlUtils.attrOut(writer, "avatarId", voteTo.getAvatarId());\r
-            writer.append(' ');\r
-            XmlUtils.attrOut(writer, "count", "" + count);\r
-            writer.append(" />\n");\r
-        }\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * playerlist子要素をXML出力する。\r
-     * @param writer 出力先\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public void dumpPlayerlistElem(Writer writer) throws IOException{\r
-        int num = this.avatarList.size();\r
-\r
-        for(int index = 0; index < num; index++){\r
-            AvatarData avatar = this.avatarList.get(index);\r
-            DecodedContent uri     = this.strList.get(index * 2);\r
-            DecodedContent account = this.strList.get(index * 2 + 1);\r
-            int isLiving = this.intList.get(index);\r
-            String survive;\r
-            if(isLiving == 0) survive = "false";\r
-            else              survive = "true";\r
-            GameRole role = this.roleList.get(index);\r
-            String roleName = getRoleAttrValue(role);\r
-\r
-            writer.append("<playerInfo");\r
-            writer.append(' ');\r
-            XmlUtils.attrOut(writer, "playerId", account.toString());\r
-            writer.append(' ');\r
-            XmlUtils.attrOut(writer, "avatarId", avatar.getAvatarId());\r
-            writer.append(' ');\r
-            XmlUtils.attrOut(writer, "survive", survive);\r
-            writer.append(' ');\r
-            XmlUtils.attrOut(writer, "role", roleName);\r
-\r
-            String uriStr = uri.toString();\r
-            uriStr = uriStr.replaceAll("^[\\s]+", "");\r
-            uriStr = uriStr.replaceAll("[\\s]+$", "");\r
-            uriStr = uriStr.replaceAll("[\\s]+", "\u0020");\r
-            if(uriStr.length() > 0){\r
-                writer.append(' ');\r
-                XmlUtils.attrOut(writer, "uri", uriStr);\r
-            }\r
-\r
-            writer.append(" />\n");\r
-        }\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * Avatar間関係の属性値をXML出力する。\r
-     * @param writer 出力先\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public void dumpByWhomAttr(Writer writer) throws IOException{\r
-        AvatarData by = this.avatarList.get(0);\r
-        AvatarData to = this.avatarList.get(1);\r
-\r
-        writer.append(' ');\r
-        XmlUtils.attrOut(writer, "byWhom", by.getAvatarId());\r
-        writer.append(' ');\r
-        XmlUtils.attrOut(writer, "target", to.getAvatarId());\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * ASSAULT属性値をXML出力する。\r
-     * @param writer 出力先\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public void dumpAssaultAttr(Writer writer) throws IOException{\r
-        AvatarData by = this.avatarList.get(0);\r
-        AvatarData to = this.avatarList.get(1);\r
-\r
-        writer.append('\n');\r
-\r
-        XmlUtils.indent(writer, 1);\r
-        XmlUtils.attrOut(writer, "byWhom", by.getAvatarId());\r
-        writer.append(' ');\r
-        XmlUtils.attrOut(writer, "target", to.getAvatarId());\r
-        writer.append('\n');\r
-\r
-        DecodedContent xname = this.strList.get(0);\r
-        XmlUtils.indent(writer, 1);\r
-        XmlUtils.attrOut(writer, "xname", xname);\r
-\r
-        int hour = this.intList.get(0);\r
-        int minute = this.intList.get(1);\r
-        writer.append(' ');\r
-        XmlUtils.timeAttrOut(writer, "time", hour, minute);\r
-        writer.append('\n');\r
-\r
-        String icon = this.strList.get(1).toString();\r
-        if( ! icon.equals(by.getFaceIconUri()) ){\r
-            XmlUtils.indent(writer, 1);\r
-            XmlUtils.attrOut(writer, "faceIconURI", icon);\r
-            writer.append('\n');\r
-        }\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * システムイベント各種要素のXML出力を行う。\r
-     * @param writer 出力先\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public void dumpXml(Writer writer) throws IOException{\r
-        String tagName = getTagName(this.eventType);\r
-\r
-        writer.append("<");\r
-        writer.append(tagName);\r
-\r
-        boolean hasAttr = true;\r
-        switch(this.eventType){\r
-        case ONSTAGE:\r
-            dumpOnstageAttr(writer);\r
-            break;\r
-        case COUNTING:\r
-            dumpCountingAttr(writer);\r
-            break;\r
-        case EXECUTION:\r
-            dumpExecutionAttr(writer);\r
-            break;\r
-        case SUDDENDEATH:\r
-        case VANISH:\r
-        case CHECKOUT:\r
-            dumpSingleAvatarAttr(writer);\r
-            break;\r
-        case ASKENTRY:\r
-            dumpAskEntryAttr(writer);\r
-            break;\r
-        case ASKCOMMIT:\r
-            dumpAskCommitAttr(writer);\r
-            break;\r
-        case STAYEPILOGUE:\r
-            dumpStayEpilogueAttr(writer);\r
-            break;\r
-        case JUDGE:\r
-        case GUARD:\r
-            dumpByWhomAttr(writer);\r
-            break;\r
-        case ASSAULT:\r
-            dumpAssaultAttr(writer);\r
-            break;\r
-        default:\r
-            hasAttr = false;\r
-            break;\r
-        }\r
-\r
-        if(hasAttr) writer.append(' ');\r
-        writer.append(">\n");\r
-\r
-        dumpLines(writer);\r
-\r
-        switch(this.eventType){\r
-        case OPENROLE:\r
-            dumpOpenroleElem(writer);\r
-            break;\r
-        case MURDERED:\r
-            dumpMurderedElem(writer);\r
-            break;\r
-        case SURVIVOR:\r
-            dumpSurvivorElem(writer);\r
-            break;\r
-        case COUNTING:\r
-        case COUNTING2:\r
-            dumpCountingElem(writer);\r
-            break;\r
-        case EXECUTION:\r
-            dumpExecutionElem(writer);\r
-            break;\r
-        case PLAYERLIST:\r
-            dumpPlayerlistElem(writer);\r
-            break;\r
-        case NOCOMMENT:\r
-            dumpNoCommentElem(writer);\r
-            break;\r
-        default:\r
-            break;\r
-        }\r
-\r
-        writer.append("</");\r
-        writer.append(tagName);\r
-        writer.append(">\n");\r
-\r
-        return;\r
-    }\r
-\r
-}\r
+/*
+ * system event
+ *
+ * License : The MIT License
+ * Copyright(c) 2008 olyutorskii
+ */
+
+package jp.sourceforge.jindolf.archiver;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.LinkedList;
+import java.util.List;
+import jp.sourceforge.jindolf.corelib.GameRole;
+import jp.sourceforge.jindolf.corelib.SysEventType;
+import jp.sourceforge.jindolf.parser.DecodedContent;
+
+/**
+ * システムイベント モデル。
+ */
+public class EventData extends TopicData{
+
+    private SysEventType eventType = null;
+
+    private final List<AvatarData> avatarList = new LinkedList<AvatarData>();
+    private final List<Integer> intList = new LinkedList<Integer>();
+    private final List<GameRole> roleList = new LinkedList<GameRole>();
+    private final List<DecodedContent> strList =
+            new LinkedList<DecodedContent>();
+
+
+    /**
+     * コンストラクタ。
+     * @param periodData 所属元Period
+     */
+    public EventData(PeriodData periodData){
+        super();
+        return;
+    }
+
+
+    /**
+     * イベント種別からXML要素名を取得する。
+     * @param type イベント種別
+     * @return 要素名
+     */
+    public static String getTagName(SysEventType type){
+        String tagName;
+
+        switch(type){
+        case STARTENTRY:   tagName = "startEntry";   break;
+        case ONSTAGE:      tagName = "onStage";      break;
+        case STARTMIRROR:  tagName = "startMirror";  break;
+        case OPENROLE:     tagName = "openRole";     break;
+        case MURDERED:     tagName = "murdered";     break;
+        case STARTASSAULT: tagName = "startAssault"; break;
+        case SURVIVOR:     tagName = "survivor";     break;
+        case COUNTING:     tagName = "counting";     break;
+        case NOMURDER:     tagName = "noMurder";     break;
+        case SUDDENDEATH:  tagName = "suddenDeath";  break;
+        case WINVILLAGE:   tagName = "winVillage";   break;
+        case WINWOLF:      tagName = "winWolf";      break;
+        case WINHAMSTER:   tagName = "winHamster";   break;
+        case PLAYERLIST:   tagName = "playerList";   break;
+        case PANIC:        tagName = "panic";        break;
+        case EXECUTION:    tagName = "execution";    break;
+        case VANISH:       tagName = "vanish";       break;
+        case CHECKOUT:     tagName = "checkout";     break;
+        case SHORTMEMBER:  tagName = "shortMember";  break;
+        case ASKENTRY:     tagName = "askEntry";     break;
+        case ASKCOMMIT:    tagName = "askCommit";    break;
+        case NOCOMMENT:    tagName = "noComment";    break;
+        case STAYEPILOGUE: tagName = "stayEpilogue"; break;
+        case GAMEOVER:     tagName = "gameOver";     break;
+        case GUARD:        tagName = "guard";        break;
+        case JUDGE:        tagName = "judge";        break;
+        case COUNTING2:    tagName = "counting2";    break;
+        case ASSAULT:      tagName = "assault";      break;
+        default: throw new IllegalArgumentException();
+        }
+
+        return tagName;
+    }
+
+    /**
+     * 役職からXMLシンボル名を取得する。
+     * @param role 役職
+     * @return XMLシンボル名
+     */
+    public static String getRoleAttrValue(GameRole role){
+        String roleName;
+
+        switch(role){
+        case INNOCENT: roleName = "innocent"; break;
+        case WOLF:     roleName = "wolf";     break;
+        case SEER:     roleName = "seer";     break;
+        case SHAMAN:   roleName = "shaman";   break;
+        case MADMAN:   roleName = "madman";   break;
+        case HUNTER:   roleName = "hunter";   break;
+        case FRATER:   roleName = "frater";   break;
+        case HAMSTER:  roleName = "hamster";  break;
+        default: throw new IllegalArgumentException();
+        }
+
+        return roleName;
+    }
+
+    /**
+     * avatarRef要素をXML出力する。
+     * @param writer 出力先
+     * @param avatar Avatar
+     * @throws IOException 出力エラー
+     */
+    public static void dumpAvatarRef(Writer writer, AvatarData avatar)
+            throws IOException{
+        writer.append("<avatarRef");
+        writer.append(' ');
+        XmlUtils.attrOut(writer, "avatarId", avatar.getAvatarId());
+        writer.append(" />\n");
+        return;
+    }
+
+    /**
+     * システムイベント種別を取得する。
+     * @return システムイベント種別
+     */
+    public SysEventType getEventType(){
+        return this.eventType;
+    }
+
+    /**
+     * システムイベント種別を設定する。
+     * @param eventType システムイベント種別
+     */
+    public void setEventType(SysEventType eventType){
+        this.eventType = eventType;
+        return;
+    }
+
+    /**
+     * Avatar情報を追加する。
+     * @param avatarData Avatar情報
+     */
+    public void addAvatarData(AvatarData avatarData){
+        this.avatarList.add(avatarData);
+        return;
+    }
+
+    /**
+     * 整数情報を追加する。
+     * @param intVal 整数情報
+     */
+    public void addInteger(int intVal){
+        this.intList.add(intVal);
+        return;
+    }
+
+    /**
+     * 役職情報を追加する。
+     * @param role 役職情報
+     */
+    public void addGameRole(GameRole role){
+        this.roleList.add(role);
+        return;
+    }
+
+    /**
+     * 文字列情報を追加する。
+     * @param seq 文字列情報
+     */
+    public void addDecodedContent(DecodedContent seq){
+        this.strList.add(seq);
+        return;
+    }
+
+    /**
+     * ONSTAGE属性値をXML出力する。
+     * @param writer 出力先
+     * @throws IOException 出力エラー
+     */
+    public void dumpOnstageAttr(Writer writer) throws IOException{
+        int entryNo = this.intList.get(0);
+        AvatarData avatarData = this.avatarList.get(0);
+
+        writer.append(' ');
+        XmlUtils.attrOut(writer, "entryNo", Integer.toString(entryNo));
+        writer.append(' ');
+        XmlUtils.attrOut(writer, "avatarId", avatarData.getAvatarId());
+
+        return;
+    }
+
+    /**
+     * avatarId属性値をXML出力する。
+     * @param writer 出力先
+     * @throws IOException 出力エラー
+     */
+    public void dumpSingleAvatarAttr(Writer writer) throws IOException{
+        AvatarData avatarData = this.avatarList.get(0);
+
+        writer.append(' ');
+        XmlUtils.attrOut(writer, "avatarId", avatarData.getAvatarId());
+
+        return;
+    }
+
+    /**
+     * COUNTINGのvictim属性値をXML出力する。
+     * @param writer 出力先
+     * @throws IOException 出力エラー
+     */
+    public void dumpCountingAttr(Writer writer) throws IOException{
+        int total = this.avatarList.size();
+        if(total % 2 != 0){
+            AvatarData victim = this.avatarList.get(total - 1);
+            writer.append(' ');
+            XmlUtils.attrOut(writer, "victim", victim.getAvatarId());
+        }
+        return;
+    }
+
+    /**
+     * EXECUTIONのvictim属性値をXML出力する。
+     * @param writer 出力先
+     * @throws IOException 出力エラー
+     */
+    public void dumpExecutionAttr(Writer writer) throws IOException{
+        int totalAvatar = this.avatarList.size();
+        int totalVotes = this.intList.size();
+        if(totalAvatar != totalVotes){
+            AvatarData victim = this.avatarList.get(totalAvatar - 1);
+            writer.append(' ');
+            XmlUtils.attrOut(writer, "victim", victim.getAvatarId());
+        }
+        return;
+    }
+
+    /**
+     * ASKENTRY属性値をXML出力する。
+     * @param writer 出力先
+     * @throws IOException 出力エラー
+     */
+    public void dumpAskEntryAttr(Writer writer) throws IOException{
+        int hour     = this.intList.get(0);
+        int minute   = this.intList.get(1);
+        int minLimit = this.intList.get(2);
+        int maxLimit = this.intList.get(3);
+
+        writer.append(' ');
+        XmlUtils.timeAttrOut(writer, "commitTime", hour, minute);
+
+        writer.append(' ');
+        XmlUtils.attrOut(writer, "minMembers", Integer.toString(minLimit));
+
+        writer.append(' ');
+        XmlUtils.attrOut(writer, "maxMembers", Integer.toString(maxLimit));
+
+        return;
+    }
+
+    /**
+     * ASKCOMMIT属性値をXML出力する。
+     * @param writer 出力先
+     * @throws IOException 出力エラー
+     */
+    public void dumpAskCommitAttr(Writer writer) throws IOException{
+        int hour     = this.intList.get(0);
+        int minute   = this.intList.get(1);
+
+        writer.append(' ');
+        XmlUtils.timeAttrOut(writer, "limitVote", hour, minute);
+
+        writer.append(' ');
+        XmlUtils.timeAttrOut(writer, "limitSpecial", hour, minute);
+
+        return;
+    }
+
+    /**
+     * STAYEPILOGUE属性値をXML出力する。
+     * @param writer 出力先
+     * @throws IOException 出力エラー
+     */
+    public void dumpStayEpilogueAttr(Writer writer) throws IOException{
+        GameRole role = this.roleList.get(0);
+        int hour   = this.intList.get(0);
+        int minute = this.intList.get(1);
+
+        String winner;
+        switch(role){
+        case INNOCENT: winner = "village"; break;
+        case WOLF:     winner = "wolf";    break;
+        case HAMSTER:  winner = "hamster"; break;
+        default: throw new IllegalArgumentException();
+        }
+        writer.append(' ');
+        XmlUtils.attrOut(writer, "maxMembers", winner);
+
+        writer.append(' ');
+        XmlUtils.timeAttrOut(writer, "limitTime", hour, minute);
+
+        return;
+    }
+
+    /**
+     * openRole子要素をXML出力する。
+     * @param writer 出力先
+     * @throws IOException 出力エラー
+     */
+    public void dumpOpenroleElem(Writer writer) throws IOException{
+        int num = this.roleList.size();
+        for(int index = 0; index < num; index++){
+            int heads = this.intList.get(index);
+            GameRole role = this.roleList.get(index);
+            String roleName = getRoleAttrValue(role);
+
+            writer.append("<roleHeads");
+            writer.append(' ');
+            XmlUtils.attrOut(writer, "role", roleName);
+            writer.append(' ');
+            XmlUtils.attrOut(writer, "heads", Integer.toString(heads));
+            writer.append(" />\n");
+        }
+        return;
+    }
+
+    /**
+     * murdered子要素をXML出力する。
+     * @param writer 出力先
+     * @throws IOException 出力エラー
+     */
+    public void dumpMurderedElem(Writer writer) throws IOException{
+        for(AvatarData avatar : this.avatarList){
+            dumpAvatarRef(writer, avatar);
+        }
+        return;
+    }
+
+    /**
+     * survivor子要素をXML出力する。
+     * @param writer 出力先
+     * @throws IOException 出力エラー
+     */
+    public void dumpSurvivorElem(Writer writer) throws IOException{
+        for(AvatarData avatar : this.avatarList){
+            dumpAvatarRef(writer, avatar);
+        }
+        return;
+    }
+
+    /**
+     * nocomment子要素をXML出力する。
+     * @param writer 出力先
+     * @throws IOException 出力エラー
+     */
+    public void dumpNoCommentElem(Writer writer) throws IOException{
+        for(AvatarData avatar : this.avatarList){
+            dumpAvatarRef(writer, avatar);
+        }
+        return;
+    }
+
+    /**
+     * counting子要素をXML出力する。
+     * @param writer 出力先
+     * @throws IOException 出力エラー
+     */
+    public void dumpCountingElem(Writer writer) throws IOException{
+        int total = this.avatarList.size();
+        total = total / 2 * 2;
+        for(int index = 0; index < total; index += 2){
+            AvatarData voteBy = this.avatarList.get(index);
+            AvatarData voteTo = this.avatarList.get(index + 1);
+            writer.append("<vote");
+            writer.append(' ');
+            XmlUtils.attrOut(writer, "byWhom", voteBy.getAvatarId());
+            writer.append(' ');
+            XmlUtils.attrOut(writer, "target", voteTo.getAvatarId());
+            writer.append(" />\n");
+        }
+        return;
+    }
+
+    /**
+     * execution子要素をXML出力する。
+     * @param writer 出力先
+     * @throws IOException 出力エラー
+     */
+    public void dumpExecutionElem(Writer writer) throws IOException{
+        int total = this.intList.size();
+        for(int index = 0; index < total; index++){
+            AvatarData voteTo = this.avatarList.get(index);
+            int count = this.intList.get(index);
+            writer.append("<nominated");
+            writer.append(' ');
+            XmlUtils.attrOut(writer, "avatarId", voteTo.getAvatarId());
+            writer.append(' ');
+            XmlUtils.attrOut(writer, "count", "" + count);
+            writer.append(" />\n");
+        }
+        return;
+    }
+
+    /**
+     * playerlist子要素をXML出力する。
+     * @param writer 出力先
+     * @throws IOException 出力エラー
+     */
+    public void dumpPlayerlistElem(Writer writer) throws IOException{
+        int num = this.avatarList.size();
+
+        for(int index = 0; index < num; index++){
+            AvatarData avatar = this.avatarList.get(index);
+            DecodedContent uri     = this.strList.get(index * 2);
+            DecodedContent account = this.strList.get(index * 2 + 1);
+            int isLiving = this.intList.get(index);
+            String survive;
+            if(isLiving == 0) survive = "false";
+            else              survive = "true";
+            GameRole role = this.roleList.get(index);
+            String roleName = getRoleAttrValue(role);
+
+            writer.append("<playerInfo");
+            writer.append(' ');
+            XmlUtils.attrOut(writer, "playerId", account.toString());
+            writer.append(' ');
+            XmlUtils.attrOut(writer, "avatarId", avatar.getAvatarId());
+            writer.append(' ');
+            XmlUtils.attrOut(writer, "survive", survive);
+            writer.append(' ');
+            XmlUtils.attrOut(writer, "role", roleName);
+
+            String uriStr = uri.toString();
+            uriStr = uriStr.replaceAll("^[\\s]+", "");
+            uriStr = uriStr.replaceAll("[\\s]+$", "");
+            uriStr = uriStr.replaceAll("[\\s]+", "\u0020");
+            if(uriStr.length() > 0){
+                writer.append(' ');
+                XmlUtils.attrOut(writer, "uri", uriStr);
+            }
+
+            writer.append(" />\n");
+        }
+
+        return;
+    }
+
+    /**
+     * Avatar間関係の属性値をXML出力する。
+     * @param writer 出力先
+     * @throws IOException 出力エラー
+     */
+    public void dumpByWhomAttr(Writer writer) throws IOException{
+        AvatarData by = this.avatarList.get(0);
+        AvatarData to = this.avatarList.get(1);
+
+        writer.append(' ');
+        XmlUtils.attrOut(writer, "byWhom", by.getAvatarId());
+        writer.append(' ');
+        XmlUtils.attrOut(writer, "target", to.getAvatarId());
+
+        return;
+    }
+
+    /**
+     * ASSAULT属性値をXML出力する。
+     * @param writer 出力先
+     * @throws IOException 出力エラー
+     */
+    public void dumpAssaultAttr(Writer writer) throws IOException{
+        AvatarData by = this.avatarList.get(0);
+        AvatarData to = this.avatarList.get(1);
+
+        writer.append('\n');
+
+        XmlUtils.indent(writer, 1);
+        XmlUtils.attrOut(writer, "byWhom", by.getAvatarId());
+        writer.append(' ');
+        XmlUtils.attrOut(writer, "target", to.getAvatarId());
+        writer.append('\n');
+
+        DecodedContent xname = this.strList.get(0);
+        XmlUtils.indent(writer, 1);
+        XmlUtils.attrOut(writer, "xname", xname);
+
+        int hour = this.intList.get(0);
+        int minute = this.intList.get(1);
+        writer.append(' ');
+        XmlUtils.timeAttrOut(writer, "time", hour, minute);
+        writer.append('\n');
+
+        String icon = this.strList.get(1).toString();
+        if( ! icon.equals(by.getFaceIconUri()) ){
+            XmlUtils.indent(writer, 1);
+            XmlUtils.attrOut(writer, "faceIconURI", icon);
+            writer.append('\n');
+        }
+
+        return;
+    }
+
+    /**
+     * システムイベント各種要素のXML出力を行う。
+     * @param writer 出力先
+     * @throws IOException 出力エラー
+     */
+    public void dumpXml(Writer writer) throws IOException{
+        String tagName = getTagName(this.eventType);
+
+        writer.append("<");
+        writer.append(tagName);
+
+        boolean hasAttr = true;
+        switch(this.eventType){
+        case ONSTAGE:
+            dumpOnstageAttr(writer);
+            break;
+        case COUNTING:
+            dumpCountingAttr(writer);
+            break;
+        case EXECUTION:
+            dumpExecutionAttr(writer);
+            break;
+        case SUDDENDEATH:
+        case VANISH:
+        case CHECKOUT:
+            dumpSingleAvatarAttr(writer);
+            break;
+        case ASKENTRY:
+            dumpAskEntryAttr(writer);
+            break;
+        case ASKCOMMIT:
+            dumpAskCommitAttr(writer);
+            break;
+        case STAYEPILOGUE:
+            dumpStayEpilogueAttr(writer);
+            break;
+        case JUDGE:
+        case GUARD:
+            dumpByWhomAttr(writer);
+            break;
+        case ASSAULT:
+            dumpAssaultAttr(writer);
+            break;
+        default:
+            hasAttr = false;
+            break;
+        }
+
+        if(hasAttr) writer.append(' ');
+        writer.append(">\n");
+
+        dumpLines(writer);
+
+        switch(this.eventType){
+        case OPENROLE:
+            dumpOpenroleElem(writer);
+            break;
+        case MURDERED:
+            dumpMurderedElem(writer);
+            break;
+        case SURVIVOR:
+            dumpSurvivorElem(writer);
+            break;
+        case COUNTING:
+        case COUNTING2:
+            dumpCountingElem(writer);
+            break;
+        case EXECUTION:
+            dumpExecutionElem(writer);
+            break;
+        case PLAYERLIST:
+            dumpPlayerlistElem(writer);
+            break;
+        case NOCOMMENT:
+            dumpNoCommentElem(writer);
+            break;
+        default:
+            break;
+        }
+
+        writer.append("</");
+        writer.append(tagName);
+        writer.append(">\n");
+
+        return;
+    }
+
+}
index a79508b..c13fa8a 100644 (file)
-/*\r
- * file archive utilities\r
- *\r
- * License : The MIT License\r
- * Copyright(c) 2008 olyutorskii\r
- */\r
-\r
-package jp.sourceforge.jindolf.archiver;\r
-\r
-import java.io.File;\r
-import java.io.IOException;\r
-import java.io.InputStream;\r
-import java.io.InputStreamReader;\r
-import java.io.LineNumberReader;\r
-import java.io.Reader;\r
-import java.net.MalformedURLException;\r
-import java.net.URI;\r
-import java.net.URL;\r
-import java.text.DateFormat;\r
-import java.text.ParseException;\r
-import java.text.SimpleDateFormat;\r
-import java.util.Date;\r
-import java.util.LinkedList;\r
-import java.util.List;\r
-import java.util.Locale;\r
-import java.util.regex.Matcher;\r
-import java.util.regex.Pattern;\r
-import jp.sourceforge.jindolf.corelib.LandDef;\r
-import jp.sourceforge.jindolf.corelib.PeriodType;\r
-\r
-/**\r
- * ファイルシステム上に納められた\r
- * 生XHTMLデータおよびログファイルへのアクセス諸々。\r
- */\r
-public final class FileArchive{\r
-\r
-    private static final Pattern LINE_PATTERN;\r
-    private static final DateFormat ISO_FORMAT;\r
-\r
-    static{\r
-        String fnameRegex =\r
-                 "(jin_([^_]+)_(\\d+)_(\\d+)_"\r
-                +"(?:(prologue)|(progress)|(epilogue))"\r
-                +"\\.html)";\r
-        LINE_PATTERN = Pattern.compile(\r
-            "^" + fnameRegex + "\\s+(\\S+)\\s+(\\S+)\\s+(\\d)" + "$");\r
-\r
-        ISO_FORMAT =\r
-                new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.JAPAN);\r
-    }\r
-\r
-\r
-    /**\r
-     * 隠れコンストラクタ。\r
-     */\r
-    private FileArchive(){\r
-        throw new Error();\r
-    }\r
-\r
-\r
-    /**\r
-     * ISO形式の日付時刻情報をパースする。\r
-     * @param text 日付表記\r
-     * @return エポック秒(ms)\r
-     */\r
-    public static long parseISODate(String text){\r
-        Date date;\r
-        try{\r
-            synchronized(ISO_FORMAT){\r
-                date = ISO_FORMAT.parse(text);\r
-            }\r
-        }catch(ParseException e){\r
-            throw new IllegalArgumentException(e);\r
-        }\r
-        return date.getTime();\r
-    }\r
-\r
-    /**\r
-     * ログ記述からリソース情報を生成する。\r
-     * @param logLine 1行に納められたログ記述\r
-     * @return リソース情報\r
-     */\r
-    public static PeriodResource parseDownLogLine(CharSequence logLine){\r
-        PeriodResource result;\r
-\r
-        Matcher matcher = LINE_PATTERN.matcher(logLine);\r
-        if( ! matcher.matches() ) throw new IllegalArgumentException();\r
-\r
-        String fname = matcher.group(1);\r
-        String landId = matcher.group(2);\r
-        int villageId = Integer.parseInt(matcher.group(3));\r
-        int day = Integer.parseInt(matcher.group(4));\r
-\r
-        PeriodType periodType;\r
-        if     (matcher.start(5) >= 0) periodType = PeriodType.PROLOGUE;\r
-        else if(matcher.start(6) >= 0) periodType = PeriodType.PROGRESS;\r
-        else if(matcher.start(7) >= 0) periodType = PeriodType.EPILOGUE;\r
-        else throw new IllegalArgumentException();\r
-\r
-        String uriText = matcher.group(8);\r
-        String dateText = matcher.group(9);\r
-        int hasError = Integer.parseInt(matcher.group(10));\r
-        if(hasError != 0) throw new IllegalArgumentException();\r
-\r
-        long dateMs = parseISODate(dateText);\r
-\r
-        if(landId.equals("wolf0")) landId = "wolf";\r
-        if(landId.equals("wolf1")) landId = "wolf0";\r
-        LandDef landDef = JinArchiver.getLandDef(landId);\r
-\r
-        File file = new File(fname);\r
-        URI fileUri = file.toURI();\r
-        URL fileUrl;\r
-        try{\r
-            fileUrl = fileUri.toURL();\r
-        }catch(MalformedURLException e){\r
-            throw new IllegalArgumentException(e);\r
-        }\r
-\r
-        result = new PeriodResource(landDef,\r
-                                    villageId,\r
-                                    periodType,\r
-                                    day,\r
-                                    uriText,\r
-                                    dateMs,\r
-                                    fileUrl );\r
-\r
-        return result;\r
-    }\r
-\r
-    /**\r
-     * ログファイルからリソース列を抽出する。\r
-     * @param reader ログファイルの内容\r
-     * @return リソース列\r
-     * @throws IOException 入力エラー\r
-     */\r
-    public static List<PeriodResource> parseDownList(LineNumberReader reader)\r
-            throws IOException{\r
-        List<PeriodResource> result = new LinkedList<PeriodResource>();\r
-\r
-        for(;;){\r
-            String line = reader.readLine();\r
-            if(line == null) break;\r
-            PeriodResource info = parseDownLogLine(line);\r
-            if(info == null){\r
-                throw new IllegalArgumentException();\r
-            }\r
-            result.add(info);\r
-        }\r
-\r
-        return result;\r
-    }\r
-\r
-    /**\r
-     * ログファイルからリソース列を抽出する。\r
-     * @param istream ログファイルの内容\r
-     * @return リソース列\r
-     * @throws IOException 入力エラー\r
-     */\r
-    public static List<PeriodResource> parseDownloadLog(InputStream istream)\r
-            throws IOException{\r
-        Reader reader = new InputStreamReader(istream, "US-ASCII");\r
-        LineNumberReader lineReader = new LineNumberReader(reader);\r
-        List<PeriodResource> result = parseDownList(lineReader);\r
-        lineReader.close();\r
-        return result;\r
-    }\r
-\r
-}\r
+/*
+ * file archive utilities
+ *
+ * License : The MIT License
+ * Copyright(c) 2008 olyutorskii
+ */
+
+package jp.sourceforge.jindolf.archiver;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.LineNumberReader;
+import java.io.Reader;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import jp.sourceforge.jindolf.corelib.LandDef;
+import jp.sourceforge.jindolf.corelib.PeriodType;
+
+/**
+ * ファイルシステム上に納められた
+ * 生XHTMLデータおよびログファイルへのアクセス諸々。
+ */
+public final class FileArchive{
+
+    private static final Pattern LINE_PATTERN;
+    private static final DateFormat ISO_FORMAT;
+
+    static{
+        String fnameRegex =
+                 "(jin_([^_]+)_(\\d+)_(\\d+)_"
+                +"(?:(prologue)|(progress)|(epilogue))"
+                +"\\.html)";
+        LINE_PATTERN = Pattern.compile(
+            "^" + fnameRegex + "\\s+(\\S+)\\s+(\\S+)\\s+(\\d)" + "$");
+
+        ISO_FORMAT =
+                new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.JAPAN);
+    }
+
+
+    /**
+     * 隠れコンストラクタ。
+     */
+    private FileArchive(){
+        throw new Error();
+    }
+
+
+    /**
+     * ISO形式の日付時刻情報をパースする。
+     * @param text 日付表記
+     * @return エポック秒(ms)
+     */
+    public static long parseISODate(String text){
+        Date date;
+        try{
+            synchronized(ISO_FORMAT){
+                date = ISO_FORMAT.parse(text);
+            }
+        }catch(ParseException e){
+            throw new IllegalArgumentException(e);
+        }
+        return date.getTime();
+    }
+
+    /**
+     * ログ記述からリソース情報を生成する。
+     * @param logLine 1行に納められたログ記述
+     * @return リソース情報
+     */
+    public static PeriodResource parseDownLogLine(CharSequence logLine){
+        PeriodResource result;
+
+        Matcher matcher = LINE_PATTERN.matcher(logLine);
+        if( ! matcher.matches() ) throw new IllegalArgumentException();
+
+        String fname = matcher.group(1);
+        String landId = matcher.group(2);
+        int villageId = Integer.parseInt(matcher.group(3));
+        int day = Integer.parseInt(matcher.group(4));
+
+        PeriodType periodType;
+        if     (matcher.start(5) >= 0) periodType = PeriodType.PROLOGUE;
+        else if(matcher.start(6) >= 0) periodType = PeriodType.PROGRESS;
+        else if(matcher.start(7) >= 0) periodType = PeriodType.EPILOGUE;
+        else throw new IllegalArgumentException();
+
+        String uriText = matcher.group(8);
+        String dateText = matcher.group(9);
+        int hasError = Integer.parseInt(matcher.group(10));
+        if(hasError != 0) throw new IllegalArgumentException();
+
+        long dateMs = parseISODate(dateText);
+
+        if(landId.equals("wolf0")) landId = "wolf";
+        if(landId.equals("wolf1")) landId = "wolf0";
+        LandDef landDef = JinArchiver.getLandDef(landId);
+
+        File file = new File(fname);
+        URI fileUri = file.toURI();
+        URL fileUrl;
+        try{
+            fileUrl = fileUri.toURL();
+        }catch(MalformedURLException e){
+            throw new IllegalArgumentException(e);
+        }
+
+        result = new PeriodResource(landDef,
+                                    villageId,
+                                    periodType,
+                                    day,
+                                    uriText,
+                                    dateMs,
+                                    fileUrl );
+
+        return result;
+    }
+
+    /**
+     * ログファイルからリソース列を抽出する。
+     * @param reader ログファイルの内容
+     * @return リソース列
+     * @throws IOException 入力エラー
+     */
+    public static List<PeriodResource> parseDownList(LineNumberReader reader)
+            throws IOException{
+        List<PeriodResource> result = new LinkedList<PeriodResource>();
+
+        for(;;){
+            String line = reader.readLine();
+            if(line == null) break;
+            PeriodResource info = parseDownLogLine(line);
+            if(info == null){
+                throw new IllegalArgumentException();
+            }
+            result.add(info);
+        }
+
+        return result;
+    }
+
+    /**
+     * ログファイルからリソース列を抽出する。
+     * @param istream ログファイルの内容
+     * @return リソース列
+     * @throws IOException 入力エラー
+     */
+    public static List<PeriodResource> parseDownloadLog(InputStream istream)
+            throws IOException{
+        Reader reader = new InputStreamReader(istream, "US-ASCII");
+        LineNumberReader lineReader = new LineNumberReader(reader);
+        List<PeriodResource> result = parseDownList(lineReader);
+        lineReader.close();
+        return result;
+    }
+
+}
index 28f5b72..3bcb883 100644 (file)
-/*\r
- * parse handler\r
- *\r
- * License : The MIT License\r
- * Copyright(c) 2008 olyutorskii\r
- */\r
-\r
-package jp.sourceforge.jindolf.archiver;\r
-\r
-import java.util.regex.Matcher;\r
-import java.util.regex.Pattern;\r
-import jp.sourceforge.jindolf.corelib.DisclosureType;\r
-import jp.sourceforge.jindolf.corelib.EventFamily;\r
-import jp.sourceforge.jindolf.corelib.GameRole;\r
-import jp.sourceforge.jindolf.corelib.LandDef;\r
-import jp.sourceforge.jindolf.corelib.PeriodType;\r
-import jp.sourceforge.jindolf.corelib.SysEventType;\r
-import jp.sourceforge.jindolf.corelib.TalkType;\r
-import jp.sourceforge.jindolf.corelib.Team;\r
-import jp.sourceforge.jindolf.corelib.VillageTag;\r
-import jp.sourceforge.jindolf.parser.DecodedContent;\r
-import jp.sourceforge.jindolf.parser.EntityConverter;\r
-import jp.sourceforge.jindolf.parser.HtmlAdapter;\r
-import jp.sourceforge.jindolf.parser.HtmlParseException;\r
-import jp.sourceforge.jindolf.parser.PageType;\r
-import jp.sourceforge.jindolf.parser.SeqRange;\r
-\r
-/**\r
- * パーサ用ハンドラ。\r
- */\r
-public class Handler extends HtmlAdapter{\r
-\r
-    private static final Pattern MURDER_PATTERN =\r
-            Pattern.compile("^(.*)\u0020!\u0020今日がお前の命日だ!$");\r
-\r
-    private final EntityConverter converter = new EntityConverter();\r
-\r
-    private VillageData villageData = null;\r
-    private String pageTitle = null;\r
-\r
-    private PeriodData currentPeriod = null;\r
-    private PeriodResource currentResource = null;\r
-\r
-    private TalkData currentTalk = null;\r
-    private EventData currentEvent = null;\r
-\r
-    /**\r
-     * コンストラクタ。\r
-     */\r
-    public Handler(){\r
-        super();\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 村情報を初期化。\r
-     * @param villageDataArg 村情報\r
-     */\r
-    public void initVillageData(VillageData villageDataArg){\r
-        this.villageData = villageDataArg;\r
-        this.currentPeriod = null;\r
-        this.pageTitle = null;\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * Periodリソース情報を初期化。\r
-     * @param resource リソース情報\r
-     */\r
-    public void initPeriodResource(PeriodResource resource){\r
-        this.currentResource = resource;\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param content {@inheritDoc}\r
-     * @throws HtmlParseException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void startParse(DecodedContent content) throws HtmlParseException{\r
-        if(this.villageData == null) throw new HtmlParseException();\r
-        this.currentPeriod =\r
-                new PeriodData(this.villageData, this.currentResource);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param content {@inheritDoc}\r
-     * @param titleRange {@inheritDoc}\r
-     * @throws HtmlParseException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void pageTitle(DecodedContent content, SeqRange titleRange)\r
-            throws HtmlParseException{\r
-        DecodedContent title = this.converter.convert(content, titleRange);\r
-        if(this.pageTitle == null){\r
-            this.pageTitle = title.toString();\r
-        }else{\r
-            if( ! this.pageTitle.contentEquals(title) ){\r
-                throw new HtmlParseException();\r
-            }\r
-        }\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param content {@inheritDoc}\r
-     * @param loginRange {@inheritDoc}\r
-     * @throws HtmlParseException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void loginName(DecodedContent content, SeqRange loginRange)\r
-            throws HtmlParseException{\r
-        DecodedContent account = this.converter.convert(content, loginRange);\r
-        this.currentPeriod.setLoginName(account);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param type {@inheritDoc}\r
-     * @throws HtmlParseException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void pageType(PageType type) throws HtmlParseException{\r
-        if(type != PageType.PERIOD_PAGE) throw new HtmlParseException();\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param content {@inheritDoc}\r
-     * @param villageRange {@inheritDoc}\r
-     * @throws HtmlParseException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void villageName(DecodedContent content, SeqRange villageRange)\r
-            throws HtmlParseException{\r
-        String vName =\r
-                this.converter.convert(content, villageRange).toString();\r
-        String fullName = this.villageData.getFullName();\r
-        if(fullName.length() <= 0){\r
-            if( ! this.pageTitle.endsWith(vName) ){\r
-                throw new HtmlParseException();\r
-            }\r
-            Pattern ptn = Pattern.compile("^([^0-9]*)([0-9]+)\\s+(\\S+)$");\r
-            Matcher matcher = ptn.matcher(vName);\r
-            if( ! matcher.matches() ) throw new HtmlParseException();\r
-            String prefix = matcher.group(1);\r
-            String vid    = matcher.group(2);\r
-            String vtag   = matcher.group(3);\r
-            LandDef landDef = this.villageData.getLandDef();\r
-            if( ! prefix.equals(landDef.getLandPrefix()) ){\r
-                throw new HtmlParseException();\r
-            }\r
-            if( Integer.parseInt(vid) != this.villageData.getVillageId() ){\r
-                throw new HtmlParseException();\r
-            }\r
-            matcher.reset(vtag);\r
-            if(VillageTag.lookingAtVillageTag(matcher) == null){\r
-                throw new HtmlParseException();\r
-            }\r
-            this.villageData.setFullName(vName);\r
-        }else{\r
-            if( ! vName.equals(fullName) ) throw new HtmlParseException();\r
-        }\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param month {@inheritDoc}\r
-     * @param day {@inheritDoc}\r
-     * @param hour {@inheritDoc}\r
-     * @param minute {@inheritDoc}\r
-     * @throws HtmlParseException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void commitTime(int month, int day, int hour, int minute)\r
-            throws HtmlParseException{\r
-        int commitHour   = this.villageData.getCommitHour();\r
-        int commitMinute = this.villageData.getCommitMinute();\r
-\r
-        if(commitHour < 0){\r
-            this.villageData.setCommitHour(hour);\r
-        }else{\r
-            if(hour != commitHour) throw new HtmlParseException();\r
-        }\r
-\r
-        if(commitMinute < 0){\r
-            this.villageData.setCommitMinute(minute);\r
-        }else{\r
-            if(minute != commitMinute) throw new HtmlParseException();\r
-        }\r
-\r
-        this.currentPeriod.setCommitMonth(month);\r
-        this.currentPeriod.setCommitDay(day);\r
-        this.currentPeriod.setCommitHour(hour);\r
-        this.currentPeriod.setCommitMinute(minute);\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param content {@inheritDoc}\r
-     * @param anchorRange {@inheritDoc}\r
-     * @param periodType {@inheritDoc}\r
-     * @param day {@inheritDoc}\r
-     * @throws HtmlParseException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void periodLink(DecodedContent content,\r
-                            SeqRange anchorRange,\r
-                            PeriodType periodType,\r
-                            int day)\r
-            throws HtmlParseException{\r
-        if(anchorRange.isValid()){\r
-            DisclosureType newType;\r
-\r
-            if(periodType == null){\r
-                newType = DisclosureType.COMPLETE;\r
-            }else if(periodType == PeriodType.EPILOGUE\r
-               &&    this.currentResource.getPeriodType()\r
-                  != PeriodType.EPILOGUE){\r
-                newType = DisclosureType.COMPLETE;\r
-            }else if(   periodType != PeriodType.PROLOGUE\r
-               &&    this.currentResource.getPeriodType()\r
-                  == PeriodType.PROLOGUE){\r
-                newType = DisclosureType.COMPLETE;\r
-            }else{\r
-                newType = DisclosureType.UNCOMPLETE;\r
-            }\r
-\r
-            this.currentPeriod.setDisclosureType(newType);\r
-\r
-            return;\r
-        }\r
-\r
-        if(periodType != this.currentResource.getPeriodType()){\r
-            throw new HtmlParseException();\r
-        }\r
-\r
-        if(periodType == PeriodType.PROGRESS\r
-                && day != this.currentResource.getDay()){\r
-            throw new HtmlParseException();\r
-        }\r
-\r
-        this.currentPeriod.setDisclosureType(DisclosureType.HOT);\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @throws HtmlParseException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void startTalk() throws HtmlParseException{\r
-        this.currentTalk = new TalkData();\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param content {@inheritDoc}\r
-     * @param avatarRange {@inheritDoc}\r
-     * @throws HtmlParseException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void talkAvatar(DecodedContent content, SeqRange avatarRange)\r
-            throws HtmlParseException{\r
-        DecodedContent avatarName =\r
-                this.converter.convert(content, avatarRange);\r
-        AvatarData avatar = this.villageData.getAvatarData(avatarName);\r
-        this.currentTalk.setAvatarData(avatar);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param content {@inheritDoc}\r
-     * @param urlRange {@inheritDoc}\r
-     * @throws HtmlParseException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void talkIconUrl(DecodedContent content, SeqRange urlRange)\r
-            throws HtmlParseException{\r
-        DecodedContent faceIcon = this.converter.convert(content, urlRange);\r
-        this.currentTalk.setFaceIconUri(faceIcon.toString());\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param content {@inheritDoc}\r
-     * @param idRange {@inheritDoc}\r
-     * @throws HtmlParseException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void talkId(DecodedContent content, SeqRange idRange)\r
-            throws HtmlParseException{\r
-        DecodedContent xname = this.converter.convert(content, idRange);\r
-        this.currentTalk.setXName(xname.toString());\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param hour {@inheritDoc}\r
-     * @param minute {@inheritDoc}\r
-     * @throws HtmlParseException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void talkTime(int hour, int minute) throws HtmlParseException{\r
-        this.currentTalk.setHour(hour);\r
-        this.currentTalk.setMinute(minute);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param type {@inheritDoc}\r
-     * @throws HtmlParseException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void talkType(TalkType type) throws HtmlParseException{\r
-        this.currentTalk.setTalkType(type);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param content {@inheritDoc}\r
-     * @param textRange {@inheritDoc}\r
-     * @throws HtmlParseException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void talkText(DecodedContent content, SeqRange textRange)\r
-            throws HtmlParseException{\r
-        DecodedContent line = this.converter.convert(content, textRange);\r
-        this.currentTalk.addLine(line);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @throws HtmlParseException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void talkBreak() throws HtmlParseException{\r
-        this.currentTalk.addBreak();\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @throws HtmlParseException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void endTalk() throws HtmlParseException{\r
-        String faceIcon = this.currentTalk.getFaceIconUri();\r
-        if(this.currentTalk.getTalkType() == TalkType.GRAVE){\r
-            this.villageData.setGraveIconUri(faceIcon);\r
-        }else{\r
-            AvatarData avatar = this.currentTalk.getAvatarData();\r
-            if(avatar.getFaceIconUri() == null){\r
-                avatar.setFaceIconUri(faceIcon);\r
-            }\r
-        }\r
-\r
-        if(   ! this.currentPeriod.hasMurderResult()\r
-           && this.currentTalk.getTalkType() == TalkType.WOLFONLY\r
-           && this.currentTalk.getLineNum() == 1){\r
-            DecodedContent line1st = this.currentTalk.get1stLine();\r
-            Matcher matcher = MURDER_PATTERN.matcher(line1st);\r
-            if(matcher.matches()){\r
-                AvatarData byWhom = this.currentTalk.getAvatarData();\r
-                String avatarName = matcher.group(1);\r
-                AvatarData target =\r
-                        this.villageData.getAvatarData(avatarName);\r
-                String xname = this.currentTalk.getXName();\r
-                int hour = this.currentTalk.getHour();\r
-                int minute = this.currentTalk.getMinute();\r
-                String iconUri = this.currentTalk.getFaceIconUri();\r
-                EventData event = new EventData(this.currentPeriod);\r
-                event.setEventType(SysEventType.ASSAULT);\r
-                event.addLine(line1st);\r
-                event.addAvatarData(byWhom);\r
-                event.addAvatarData(target);\r
-                event.addDecodedContent(new DecodedContent(xname));\r
-                event.addInteger(hour);\r
-                event.addInteger(minute);\r
-                event.addDecodedContent(new DecodedContent(iconUri));\r
-                if(byWhom.getFaceIconUri() == null){\r
-                    byWhom.setFaceIconUri(iconUri);\r
-                }\r
-                this.currentPeriod.addTopicData(event);\r
-                this.currentTalk = null;\r
-                return;\r
-            }\r
-        }\r
-\r
-        this.currentPeriod.addTopicData(this.currentTalk);\r
-        this.currentTalk = null;\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param eventFamily {@inheritDoc}\r
-     * @throws HtmlParseException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void startSysEvent(EventFamily eventFamily)\r
-            throws HtmlParseException{\r
-        this.currentEvent = new EventData(this.currentPeriod);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param type {@inheritDoc}\r
-     * @throws HtmlParseException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void sysEventType(SysEventType type) throws HtmlParseException{\r
-        this.currentEvent.setEventType(type);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param content {@inheritDoc}\r
-     * @param contentRange {@inheritDoc}\r
-     * @throws HtmlParseException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void sysEventContent(DecodedContent content,\r
-                                  SeqRange contentRange)\r
-            throws HtmlParseException{\r
-        DecodedContent line = this.converter.convert(content, contentRange);\r
-        this.currentEvent.addLine(line);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param content {@inheritDoc}\r
-     * @param anchorRange {@inheritDoc}\r
-     * @param contentRange {@inheritDoc}\r
-     * @throws HtmlParseException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void sysEventContentAnchor(DecodedContent content,\r
-                                      SeqRange anchorRange,\r
-                                      SeqRange contentRange)\r
-            throws HtmlParseException{\r
-        DecodedContent line = this.converter.convert(content, contentRange);\r
-        this.currentEvent.addLine(line);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @throws HtmlParseException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void sysEventContentBreak() throws HtmlParseException{\r
-        this.currentEvent.addBreak();\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param content {@inheritDoc}\r
-     * @param entryNo {@inheritDoc}\r
-     * @param avatarRange {@inheritDoc}\r
-     * @throws HtmlParseException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void sysEventOnStage(DecodedContent content,\r
-                                  int entryNo,\r
-                                  SeqRange avatarRange)\r
-            throws HtmlParseException{\r
-        DecodedContent avatarName =\r
-                this.converter.convert(content, avatarRange);\r
-        AvatarData avatar = this.villageData.getAvatarData(avatarName);\r
-        this.currentEvent.addAvatarData(avatar);\r
-        this.currentEvent.addInteger(entryNo);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param role {@inheritDoc}\r
-     * @param num {@inheritDoc}\r
-     * @throws HtmlParseException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void sysEventOpenRole(GameRole role, int num)\r
-            throws HtmlParseException{\r
-        this.currentEvent.addGameRole(role);\r
-        this.currentEvent.addInteger(num);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param content {@inheritDoc}\r
-     * @param avatarRange {@inheritDoc}\r
-     * @throws HtmlParseException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void sysEventMurdered(DecodedContent content,\r
-                                   SeqRange avatarRange)\r
-            throws HtmlParseException{\r
-        DecodedContent avatarName =\r
-                this.converter.convert(content, avatarRange);\r
-        AvatarData avatar = this.villageData.getAvatarData(avatarName);\r
-        this.currentEvent.addAvatarData(avatar);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param content {@inheritDoc}\r
-     * @param avatarRange {@inheritDoc}\r
-     * @throws HtmlParseException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void sysEventSurvivor(DecodedContent content,\r
-                                   SeqRange avatarRange)\r
-            throws HtmlParseException{\r
-        DecodedContent avatarName =\r
-                this.converter.convert(content, avatarRange);\r
-        AvatarData avatar = this.villageData.getAvatarData(avatarName);\r
-        this.currentEvent.addAvatarData(avatar);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param content {@inheritDoc}\r
-     * @param voteByRange {@inheritDoc}\r
-     * @param voteToRange {@inheritDoc}\r
-     * @throws HtmlParseException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void sysEventCounting(DecodedContent content,\r
-                                   SeqRange voteByRange,\r
-                                   SeqRange voteToRange)\r
-            throws HtmlParseException{\r
-        AvatarData avatar;\r
-\r
-        if(voteByRange.isValid()){\r
-            DecodedContent voteBy =\r
-                    this.converter.convert(content, voteByRange);\r
-            avatar = this.villageData.getAvatarData(voteBy);\r
-            this.currentEvent.addAvatarData(avatar);\r
-        }\r
-\r
-        DecodedContent voteTo =\r
-                this.converter.convert(content, voteToRange);\r
-        avatar = this.villageData.getAvatarData(voteTo);\r
-        this.currentEvent.addAvatarData(avatar);\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param content {@inheritDoc}\r
-     * @param voteByRange {@inheritDoc}\r
-     * @param voteToRange {@inheritDoc}\r
-     * @throws HtmlParseException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void sysEventCounting2(DecodedContent content,\r
-                                    SeqRange voteByRange,\r
-                                    SeqRange voteToRange)\r
-            throws HtmlParseException{\r
-        AvatarData avatar;\r
-\r
-        DecodedContent voteBy =\r
-                this.converter.convert(content, voteByRange);\r
-        avatar = this.villageData.getAvatarData(voteBy);\r
-        this.currentEvent.addAvatarData(avatar);\r
-\r
-        DecodedContent voteTo =\r
-                this.converter.convert(content, voteToRange);\r
-        avatar = this.villageData.getAvatarData(voteTo);\r
-        this.currentEvent.addAvatarData(avatar);\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param content {@inheritDoc}\r
-     * @param avatarRange {@inheritDoc}\r
-     * @throws HtmlParseException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void sysEventSuddenDeath(DecodedContent content,\r
-                                       SeqRange avatarRange)\r
-            throws HtmlParseException{\r
-        DecodedContent avatarName =\r
-                this.converter.convert(content, avatarRange);\r
-        AvatarData avatar = this.villageData.getAvatarData(avatarName);\r
-        this.currentEvent.addAvatarData(avatar);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param content {@inheritDoc}\r
-     * @param avatarRange {@inheritDoc}\r
-     * @param anchorRange {@inheritDoc}\r
-     * @param loginRange {@inheritDoc}\r
-     * @param isLiving {@inheritDoc}\r
-     * @param role {@inheritDoc}\r
-     * @throws HtmlParseException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void sysEventPlayerList(DecodedContent content,\r
-                                      SeqRange avatarRange,\r
-                                      SeqRange anchorRange,\r
-                                      SeqRange loginRange,\r
-                                      boolean isLiving,\r
-                                      GameRole role)\r
-            throws HtmlParseException{\r
-        DecodedContent avatarName =\r
-                this.converter.convert(content, avatarRange);\r
-        AvatarData avatar = this.villageData.getAvatarData(avatarName);\r
-        this.currentEvent.addAvatarData(avatar);\r
-\r
-        DecodedContent uri;\r
-        if(anchorRange.isValid()){\r
-            uri = this.converter.convert(content, anchorRange);\r
-        }else{\r
-            uri = new DecodedContent("");\r
-        }\r
-        Win31j.supplyWin31jChar(uri);\r
-        this.currentEvent.addDecodedContent(uri);\r
-\r
-        DecodedContent account =\r
-                this.converter.convert(content, loginRange);\r
-        Win31j.supplyWin31jChar(account);\r
-        this.currentEvent.addDecodedContent(account);\r
-\r
-        if(isLiving) this.currentEvent.addInteger(1);\r
-        else         this.currentEvent.addInteger(0);\r
-\r
-        this.currentEvent.addGameRole(role);\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param hour {@inheritDoc}\r
-     * @param minute {@inheritDoc}\r
-     * @param minLimit {@inheritDoc}\r
-     * @param maxLimit {@inheritDoc}\r
-     * @throws HtmlParseException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void sysEventAskEntry(int hour, int minute,\r
-                                 int minLimit, int maxLimit)\r
-            throws HtmlParseException{\r
-        this.currentEvent.addInteger(hour);\r
-        this.currentEvent.addInteger(minute);\r
-        this.currentEvent.addInteger(minLimit);\r
-        this.currentEvent.addInteger(maxLimit);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param hour {@inheritDoc}\r
-     * @param minute {@inheritDoc}\r
-     * @throws HtmlParseException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void sysEventAskCommit(int hour, int minute)\r
-            throws HtmlParseException{\r
-        this.currentEvent.addInteger(hour);\r
-        this.currentEvent.addInteger(minute);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param content {@inheritDoc}\r
-     * @param avatarRange {@inheritDoc}\r
-     * @throws HtmlParseException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void sysEventNoComment(DecodedContent content,\r
-                                  SeqRange avatarRange)\r
-            throws HtmlParseException{\r
-        DecodedContent avatarName;\r
-        avatarName = this.converter.convert(content, avatarRange);\r
-        AvatarData avatar = this.villageData.getAvatarData(avatarName);\r
-        this.currentEvent.addAvatarData(avatar);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param winner {@inheritDoc}\r
-     * @param hour {@inheritDoc}\r
-     * @param minute {@inheritDoc}\r
-     * @throws HtmlParseException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void sysEventStayEpilogue(Team winner,\r
-                                     int hour, int minute)\r
-            throws HtmlParseException{\r
-        GameRole role;\r
-        switch(winner){\r
-        case VILLAGE: role = GameRole.INNOCENT; break;\r
-        case WOLF:    role = GameRole.WOLF;     break;\r
-        case HAMSTER: role = GameRole.HAMSTER;  break;\r
-        default: throw new IllegalArgumentException();\r
-        }\r
-\r
-        this.currentEvent.addGameRole(role);\r
-        this.currentEvent.addInteger(hour);\r
-        this.currentEvent.addInteger(minute);\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param content {@inheritDoc}\r
-     * @param guardByRange {@inheritDoc}\r
-     * @param guardToRange {@inheritDoc}\r
-     * @throws HtmlParseException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void sysEventGuard(DecodedContent content,\r
-                                SeqRange guardByRange,\r
-                                SeqRange guardToRange)\r
-            throws HtmlParseException{\r
-        DecodedContent avatarName;\r
-\r
-        avatarName = this.converter.convert(content, guardByRange);\r
-        AvatarData guardBy = this.villageData.getAvatarData(avatarName);\r
-        this.currentEvent.addAvatarData(guardBy);\r
-\r
-        avatarName = this.converter.convert(content, guardToRange);\r
-        AvatarData guardTo = this.villageData.getAvatarData(avatarName);\r
-        this.currentEvent.addAvatarData(guardTo);\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param content {@inheritDoc}\r
-     * @param judgeByRange {@inheritDoc}\r
-     * @param judgeToRange {@inheritDoc}\r
-     * @throws HtmlParseException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void sysEventJudge(DecodedContent content,\r
-                                SeqRange judgeByRange,\r
-                                SeqRange judgeToRange)\r
-            throws HtmlParseException{\r
-        DecodedContent avatarName;\r
-\r
-        avatarName = this.converter.convert(content, judgeByRange);\r
-        AvatarData judgeBy = this.villageData.getAvatarData(avatarName);\r
-        this.currentEvent.addAvatarData(judgeBy);\r
-\r
-        avatarName = this.converter.convert(content, judgeToRange);\r
-        AvatarData judgeTo = this.villageData.getAvatarData(avatarName);\r
-        this.currentEvent.addAvatarData(judgeTo);\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param content {@inheritDoc}\r
-     * @param avatarRange {@inheritDoc}\r
-     * @param votes {@inheritDoc}\r
-     * @throws HtmlParseException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void sysEventExecution(DecodedContent content,\r
-                                    SeqRange avatarRange,\r
-                                    int votes)\r
-            throws HtmlParseException{\r
-        DecodedContent avatarName;\r
-        avatarName = this.converter.convert(content, avatarRange);\r
-        AvatarData avatar = this.villageData.getAvatarData(avatarName);\r
-        this.currentEvent.addAvatarData(avatar);\r
-\r
-        if(votes > 0){\r
-            this.currentEvent.addInteger(votes);\r
-        }\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param content {@inheritDoc}\r
-     * @param avatarRange {@inheritDoc}\r
-     * @throws HtmlParseException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void sysEventCheckout(DecodedContent content,\r
-                                   SeqRange avatarRange)\r
-            throws HtmlParseException{\r
-        DecodedContent avatarName;\r
-        avatarName = this.converter.convert(content, avatarRange);\r
-        AvatarData avatar = this.villageData.getAvatarData(avatarName);\r
-        this.currentEvent.addAvatarData(avatar);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param content {@inheritDoc}\r
-     * @param avatarRange {@inheritDoc}\r
-     * @throws HtmlParseException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void sysEventVanish(DecodedContent content,\r
-                                 SeqRange avatarRange)\r
-            throws HtmlParseException{\r
-        DecodedContent avatarName;\r
-        avatarName = this.converter.convert(content, avatarRange);\r
-        AvatarData avatar = this.villageData.getAvatarData(avatarName);\r
-        this.currentEvent.addAvatarData(avatar);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @throws HtmlParseException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void endSysEvent() throws HtmlParseException{\r
-        this.currentPeriod.addTopicData(this.currentEvent);\r
-        this.currentEvent = null;\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @throws HtmlParseException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void endParse() throws HtmlParseException{\r
-        this.villageData.addPeriodData(this.currentPeriod);\r
-        this.currentPeriod = null;\r
-        return;\r
-    }\r
-\r
-}\r
+/*
+ * parse handler
+ *
+ * License : The MIT License
+ * Copyright(c) 2008 olyutorskii
+ */
+
+package jp.sourceforge.jindolf.archiver;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import jp.sourceforge.jindolf.corelib.DisclosureType;
+import jp.sourceforge.jindolf.corelib.EventFamily;
+import jp.sourceforge.jindolf.corelib.GameRole;
+import jp.sourceforge.jindolf.corelib.LandDef;
+import jp.sourceforge.jindolf.corelib.PeriodType;
+import jp.sourceforge.jindolf.corelib.SysEventType;
+import jp.sourceforge.jindolf.corelib.TalkType;
+import jp.sourceforge.jindolf.corelib.Team;
+import jp.sourceforge.jindolf.corelib.VillageTag;
+import jp.sourceforge.jindolf.parser.DecodedContent;
+import jp.sourceforge.jindolf.parser.EntityConverter;
+import jp.sourceforge.jindolf.parser.HtmlAdapter;
+import jp.sourceforge.jindolf.parser.HtmlParseException;
+import jp.sourceforge.jindolf.parser.PageType;
+import jp.sourceforge.jindolf.parser.SeqRange;
+
+/**
+ * パーサ用ハンドラ。
+ */
+public class Handler extends HtmlAdapter{
+
+    private static final Pattern MURDER_PATTERN =
+            Pattern.compile("^(.*)\u0020!\u0020今日がお前の命日だ!$");
+
+    private final EntityConverter converter = new EntityConverter();
+
+    private VillageData villageData = null;
+    private String pageTitle = null;
+
+    private PeriodData currentPeriod = null;
+    private PeriodResource currentResource = null;
+
+    private TalkData currentTalk = null;
+    private EventData currentEvent = null;
+
+    /**
+     * コンストラクタ。
+     */
+    public Handler(){
+        super();
+        return;
+    }
+
+    /**
+     * 村情報を初期化。
+     * @param villageDataArg 村情報
+     */
+    public void initVillageData(VillageData villageDataArg){
+        this.villageData = villageDataArg;
+        this.currentPeriod = null;
+        this.pageTitle = null;
+        return;
+    }
+
+    /**
+     * Periodリソース情報を初期化。
+     * @param resource リソース情報
+     */
+    public void initPeriodResource(PeriodResource resource){
+        this.currentResource = resource;
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param content {@inheritDoc}
+     * @throws HtmlParseException {@inheritDoc}
+     */
+    @Override
+    public void startParse(DecodedContent content) throws HtmlParseException{
+        if(this.villageData == null) throw new HtmlParseException();
+        this.currentPeriod =
+                new PeriodData(this.villageData, this.currentResource);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param content {@inheritDoc}
+     * @param titleRange {@inheritDoc}
+     * @throws HtmlParseException {@inheritDoc}
+     */
+    @Override
+    public void pageTitle(DecodedContent content, SeqRange titleRange)
+            throws HtmlParseException{
+        DecodedContent title = this.converter.convert(content, titleRange);
+        if(this.pageTitle == null){
+            this.pageTitle = title.toString();
+        }else{
+            if( ! this.pageTitle.contentEquals(title) ){
+                throw new HtmlParseException();
+            }
+        }
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param content {@inheritDoc}
+     * @param loginRange {@inheritDoc}
+     * @throws HtmlParseException {@inheritDoc}
+     */
+    @Override
+    public void loginName(DecodedContent content, SeqRange loginRange)
+            throws HtmlParseException{
+        DecodedContent account = this.converter.convert(content, loginRange);
+        this.currentPeriod.setLoginName(account);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param type {@inheritDoc}
+     * @throws HtmlParseException {@inheritDoc}
+     */
+    @Override
+    public void pageType(PageType type) throws HtmlParseException{
+        if(type != PageType.PERIOD_PAGE) throw new HtmlParseException();
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param content {@inheritDoc}
+     * @param villageRange {@inheritDoc}
+     * @throws HtmlParseException {@inheritDoc}
+     */
+    @Override
+    public void villageName(DecodedContent content, SeqRange villageRange)
+            throws HtmlParseException{
+        String vName =
+                this.converter.convert(content, villageRange).toString();
+        String fullName = this.villageData.getFullName();
+        if(fullName.length() <= 0){
+            if( ! this.pageTitle.endsWith(vName) ){
+                throw new HtmlParseException();
+            }
+            Pattern ptn = Pattern.compile("^([^0-9]*)([0-9]+)\\s+(\\S+)$");
+            Matcher matcher = ptn.matcher(vName);
+            if( ! matcher.matches() ) throw new HtmlParseException();
+            String prefix = matcher.group(1);
+            String vid    = matcher.group(2);
+            String vtag   = matcher.group(3);
+            LandDef landDef = this.villageData.getLandDef();
+            if( ! prefix.equals(landDef.getLandPrefix()) ){
+                throw new HtmlParseException();
+            }
+            if( Integer.parseInt(vid) != this.villageData.getVillageId() ){
+                throw new HtmlParseException();
+            }
+            matcher.reset(vtag);
+            if(VillageTag.lookingAtVillageTag(matcher) == null){
+                throw new HtmlParseException();
+            }
+            this.villageData.setFullName(vName);
+        }else{
+            if( ! vName.equals(fullName) ) throw new HtmlParseException();
+        }
+
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param month {@inheritDoc}
+     * @param day {@inheritDoc}
+     * @param hour {@inheritDoc}
+     * @param minute {@inheritDoc}
+     * @throws HtmlParseException {@inheritDoc}
+     */
+    @Override
+    public void commitTime(int month, int day, int hour, int minute)
+            throws HtmlParseException{
+        int commitHour   = this.villageData.getCommitHour();
+        int commitMinute = this.villageData.getCommitMinute();
+
+        if(commitHour < 0){
+            this.villageData.setCommitHour(hour);
+        }else{
+            if(hour != commitHour) throw new HtmlParseException();
+        }
+
+        if(commitMinute < 0){
+            this.villageData.setCommitMinute(minute);
+        }else{
+            if(minute != commitMinute) throw new HtmlParseException();
+        }
+
+        this.currentPeriod.setCommitMonth(month);
+        this.currentPeriod.setCommitDay(day);
+        this.currentPeriod.setCommitHour(hour);
+        this.currentPeriod.setCommitMinute(minute);
+
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param content {@inheritDoc}
+     * @param anchorRange {@inheritDoc}
+     * @param periodType {@inheritDoc}
+     * @param day {@inheritDoc}
+     * @throws HtmlParseException {@inheritDoc}
+     */
+    @Override
+    public void periodLink(DecodedContent content,
+                            SeqRange anchorRange,
+                            PeriodType periodType,
+                            int day)
+            throws HtmlParseException{
+        if(anchorRange.isValid()){
+            DisclosureType newType;
+
+            if(periodType == null){
+                newType = DisclosureType.COMPLETE;
+            }else if(periodType == PeriodType.EPILOGUE
+               &&    this.currentResource.getPeriodType()
+                  != PeriodType.EPILOGUE){
+                newType = DisclosureType.COMPLETE;
+            }else if(   periodType != PeriodType.PROLOGUE
+               &&    this.currentResource.getPeriodType()
+                  == PeriodType.PROLOGUE){
+                newType = DisclosureType.COMPLETE;
+            }else{
+                newType = DisclosureType.UNCOMPLETE;
+            }
+
+            this.currentPeriod.setDisclosureType(newType);
+
+            return;
+        }
+
+        if(periodType != this.currentResource.getPeriodType()){
+            throw new HtmlParseException();
+        }
+
+        if(periodType == PeriodType.PROGRESS
+                && day != this.currentResource.getDay()){
+            throw new HtmlParseException();
+        }
+
+        this.currentPeriod.setDisclosureType(DisclosureType.HOT);
+
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws HtmlParseException {@inheritDoc}
+     */
+    @Override
+    public void startTalk() throws HtmlParseException{
+        this.currentTalk = new TalkData();
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param content {@inheritDoc}
+     * @param avatarRange {@inheritDoc}
+     * @throws HtmlParseException {@inheritDoc}
+     */
+    @Override
+    public void talkAvatar(DecodedContent content, SeqRange avatarRange)
+            throws HtmlParseException{
+        DecodedContent avatarName =
+                this.converter.convert(content, avatarRange);
+        AvatarData avatar = this.villageData.getAvatarData(avatarName);
+        this.currentTalk.setAvatarData(avatar);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param content {@inheritDoc}
+     * @param urlRange {@inheritDoc}
+     * @throws HtmlParseException {@inheritDoc}
+     */
+    @Override
+    public void talkIconUrl(DecodedContent content, SeqRange urlRange)
+            throws HtmlParseException{
+        DecodedContent faceIcon = this.converter.convert(content, urlRange);
+        this.currentTalk.setFaceIconUri(faceIcon.toString());
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param content {@inheritDoc}
+     * @param idRange {@inheritDoc}
+     * @throws HtmlParseException {@inheritDoc}
+     */
+    @Override
+    public void talkId(DecodedContent content, SeqRange idRange)
+            throws HtmlParseException{
+        DecodedContent xname = this.converter.convert(content, idRange);
+        this.currentTalk.setXName(xname.toString());
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param hour {@inheritDoc}
+     * @param minute {@inheritDoc}
+     * @throws HtmlParseException {@inheritDoc}
+     */
+    @Override
+    public void talkTime(int hour, int minute) throws HtmlParseException{
+        this.currentTalk.setHour(hour);
+        this.currentTalk.setMinute(minute);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param type {@inheritDoc}
+     * @throws HtmlParseException {@inheritDoc}
+     */
+    @Override
+    public void talkType(TalkType type) throws HtmlParseException{
+        this.currentTalk.setTalkType(type);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param content {@inheritDoc}
+     * @param textRange {@inheritDoc}
+     * @throws HtmlParseException {@inheritDoc}
+     */
+    @Override
+    public void talkText(DecodedContent content, SeqRange textRange)
+            throws HtmlParseException{
+        DecodedContent line = this.converter.convert(content, textRange);
+        this.currentTalk.addLine(line);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws HtmlParseException {@inheritDoc}
+     */
+    @Override
+    public void talkBreak() throws HtmlParseException{
+        this.currentTalk.addBreak();
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws HtmlParseException {@inheritDoc}
+     */
+    @Override
+    public void endTalk() throws HtmlParseException{
+        String faceIcon = this.currentTalk.getFaceIconUri();
+        if(this.currentTalk.getTalkType() == TalkType.GRAVE){
+            this.villageData.setGraveIconUri(faceIcon);
+        }else{
+            AvatarData avatar = this.currentTalk.getAvatarData();
+            if(avatar.getFaceIconUri() == null){
+                avatar.setFaceIconUri(faceIcon);
+            }
+        }
+
+        if(   ! this.currentPeriod.hasMurderResult()
+           && this.currentTalk.getTalkType() == TalkType.WOLFONLY
+           && this.currentTalk.getLineNum() == 1){
+            DecodedContent line1st = this.currentTalk.get1stLine();
+            Matcher matcher = MURDER_PATTERN.matcher(line1st);
+            if(matcher.matches()){
+                AvatarData byWhom = this.currentTalk.getAvatarData();
+                String avatarName = matcher.group(1);
+                AvatarData target =
+                        this.villageData.getAvatarData(avatarName);
+                String xname = this.currentTalk.getXName();
+                int hour = this.currentTalk.getHour();
+                int minute = this.currentTalk.getMinute();
+                String iconUri = this.currentTalk.getFaceIconUri();
+                EventData event = new EventData(this.currentPeriod);
+                event.setEventType(SysEventType.ASSAULT);
+                event.addLine(line1st);
+                event.addAvatarData(byWhom);
+                event.addAvatarData(target);
+                event.addDecodedContent(new DecodedContent(xname));
+                event.addInteger(hour);
+                event.addInteger(minute);
+                event.addDecodedContent(new DecodedContent(iconUri));
+                if(byWhom.getFaceIconUri() == null){
+                    byWhom.setFaceIconUri(iconUri);
+                }
+                this.currentPeriod.addTopicData(event);
+                this.currentTalk = null;
+                return;
+            }
+        }
+
+        this.currentPeriod.addTopicData(this.currentTalk);
+        this.currentTalk = null;
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param eventFamily {@inheritDoc}
+     * @throws HtmlParseException {@inheritDoc}
+     */
+    @Override
+    public void startSysEvent(EventFamily eventFamily)
+            throws HtmlParseException{
+        this.currentEvent = new EventData(this.currentPeriod);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param type {@inheritDoc}
+     * @throws HtmlParseException {@inheritDoc}
+     */
+    @Override
+    public void sysEventType(SysEventType type) throws HtmlParseException{
+        this.currentEvent.setEventType(type);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param content {@inheritDoc}
+     * @param contentRange {@inheritDoc}
+     * @throws HtmlParseException {@inheritDoc}
+     */
+    @Override
+    public void sysEventContent(DecodedContent content,
+                                  SeqRange contentRange)
+            throws HtmlParseException{
+        DecodedContent line = this.converter.convert(content, contentRange);
+        this.currentEvent.addLine(line);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param content {@inheritDoc}
+     * @param anchorRange {@inheritDoc}
+     * @param contentRange {@inheritDoc}
+     * @throws HtmlParseException {@inheritDoc}
+     */
+    @Override
+    public void sysEventContentAnchor(DecodedContent content,
+                                      SeqRange anchorRange,
+                                      SeqRange contentRange)
+            throws HtmlParseException{
+        DecodedContent line = this.converter.convert(content, contentRange);
+        this.currentEvent.addLine(line);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws HtmlParseException {@inheritDoc}
+     */
+    @Override
+    public void sysEventContentBreak() throws HtmlParseException{
+        this.currentEvent.addBreak();
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param content {@inheritDoc}
+     * @param entryNo {@inheritDoc}
+     * @param avatarRange {@inheritDoc}
+     * @throws HtmlParseException {@inheritDoc}
+     */
+    @Override
+    public void sysEventOnStage(DecodedContent content,
+                                  int entryNo,
+                                  SeqRange avatarRange)
+            throws HtmlParseException{
+        DecodedContent avatarName =
+                this.converter.convert(content, avatarRange);
+        AvatarData avatar = this.villageData.getAvatarData(avatarName);
+        this.currentEvent.addAvatarData(avatar);
+        this.currentEvent.addInteger(entryNo);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param role {@inheritDoc}
+     * @param num {@inheritDoc}
+     * @throws HtmlParseException {@inheritDoc}
+     */
+    @Override
+    public void sysEventOpenRole(GameRole role, int num)
+            throws HtmlParseException{
+        this.currentEvent.addGameRole(role);
+        this.currentEvent.addInteger(num);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param content {@inheritDoc}
+     * @param avatarRange {@inheritDoc}
+     * @throws HtmlParseException {@inheritDoc}
+     */
+    @Override
+    public void sysEventMurdered(DecodedContent content,
+                                   SeqRange avatarRange)
+            throws HtmlParseException{
+        DecodedContent avatarName =
+                this.converter.convert(content, avatarRange);
+        AvatarData avatar = this.villageData.getAvatarData(avatarName);
+        this.currentEvent.addAvatarData(avatar);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param content {@inheritDoc}
+     * @param avatarRange {@inheritDoc}
+     * @throws HtmlParseException {@inheritDoc}
+     */
+    @Override
+    public void sysEventSurvivor(DecodedContent content,
+                                   SeqRange avatarRange)
+            throws HtmlParseException{
+        DecodedContent avatarName =
+                this.converter.convert(content, avatarRange);
+        AvatarData avatar = this.villageData.getAvatarData(avatarName);
+        this.currentEvent.addAvatarData(avatar);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param content {@inheritDoc}
+     * @param voteByRange {@inheritDoc}
+     * @param voteToRange {@inheritDoc}
+     * @throws HtmlParseException {@inheritDoc}
+     */
+    @Override
+    public void sysEventCounting(DecodedContent content,
+                                   SeqRange voteByRange,
+                                   SeqRange voteToRange)
+            throws HtmlParseException{
+        AvatarData avatar;
+
+        if(voteByRange.isValid()){
+            DecodedContent voteBy =
+                    this.converter.convert(content, voteByRange);
+            avatar = this.villageData.getAvatarData(voteBy);
+            this.currentEvent.addAvatarData(avatar);
+        }
+
+        DecodedContent voteTo =
+                this.converter.convert(content, voteToRange);
+        avatar = this.villageData.getAvatarData(voteTo);
+        this.currentEvent.addAvatarData(avatar);
+
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param content {@inheritDoc}
+     * @param voteByRange {@inheritDoc}
+     * @param voteToRange {@inheritDoc}
+     * @throws HtmlParseException {@inheritDoc}
+     */
+    @Override
+    public void sysEventCounting2(DecodedContent content,
+                                    SeqRange voteByRange,
+                                    SeqRange voteToRange)
+            throws HtmlParseException{
+        AvatarData avatar;
+
+        DecodedContent voteBy =
+                this.converter.convert(content, voteByRange);
+        avatar = this.villageData.getAvatarData(voteBy);
+        this.currentEvent.addAvatarData(avatar);
+
+        DecodedContent voteTo =
+                this.converter.convert(content, voteToRange);
+        avatar = this.villageData.getAvatarData(voteTo);
+        this.currentEvent.addAvatarData(avatar);
+
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param content {@inheritDoc}
+     * @param avatarRange {@inheritDoc}
+     * @throws HtmlParseException {@inheritDoc}
+     */
+    @Override
+    public void sysEventSuddenDeath(DecodedContent content,
+                                       SeqRange avatarRange)
+            throws HtmlParseException{
+        DecodedContent avatarName =
+                this.converter.convert(content, avatarRange);
+        AvatarData avatar = this.villageData.getAvatarData(avatarName);
+        this.currentEvent.addAvatarData(avatar);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param content {@inheritDoc}
+     * @param avatarRange {@inheritDoc}
+     * @param anchorRange {@inheritDoc}
+     * @param loginRange {@inheritDoc}
+     * @param isLiving {@inheritDoc}
+     * @param role {@inheritDoc}
+     * @throws HtmlParseException {@inheritDoc}
+     */
+    @Override
+    public void sysEventPlayerList(DecodedContent content,
+                                      SeqRange avatarRange,
+                                      SeqRange anchorRange,
+                                      SeqRange loginRange,
+                                      boolean isLiving,
+                                      GameRole role)
+            throws HtmlParseException{
+        DecodedContent avatarName =
+                this.converter.convert(content, avatarRange);
+        AvatarData avatar = this.villageData.getAvatarData(avatarName);
+        this.currentEvent.addAvatarData(avatar);
+
+        DecodedContent uri;
+        if(anchorRange.isValid()){
+            uri = this.converter.convert(content, anchorRange);
+        }else{
+            uri = new DecodedContent("");
+        }
+        Win31j.supplyWin31jChar(uri);
+        this.currentEvent.addDecodedContent(uri);
+
+        DecodedContent account =
+                this.converter.convert(content, loginRange);
+        Win31j.supplyWin31jChar(account);
+        this.currentEvent.addDecodedContent(account);
+
+        if(isLiving) this.currentEvent.addInteger(1);
+        else         this.currentEvent.addInteger(0);
+
+        this.currentEvent.addGameRole(role);
+
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param hour {@inheritDoc}
+     * @param minute {@inheritDoc}
+     * @param minLimit {@inheritDoc}
+     * @param maxLimit {@inheritDoc}
+     * @throws HtmlParseException {@inheritDoc}
+     */
+    @Override
+    public void sysEventAskEntry(int hour, int minute,
+                                 int minLimit, int maxLimit)
+            throws HtmlParseException{
+        this.currentEvent.addInteger(hour);
+        this.currentEvent.addInteger(minute);
+        this.currentEvent.addInteger(minLimit);
+        this.currentEvent.addInteger(maxLimit);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param hour {@inheritDoc}
+     * @param minute {@inheritDoc}
+     * @throws HtmlParseException {@inheritDoc}
+     */
+    @Override
+    public void sysEventAskCommit(int hour, int minute)
+            throws HtmlParseException{
+        this.currentEvent.addInteger(hour);
+        this.currentEvent.addInteger(minute);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param content {@inheritDoc}
+     * @param avatarRange {@inheritDoc}
+     * @throws HtmlParseException {@inheritDoc}
+     */
+    @Override
+    public void sysEventNoComment(DecodedContent content,
+                                  SeqRange avatarRange)
+            throws HtmlParseException{
+        DecodedContent avatarName;
+        avatarName = this.converter.convert(content, avatarRange);
+        AvatarData avatar = this.villageData.getAvatarData(avatarName);
+        this.currentEvent.addAvatarData(avatar);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param winner {@inheritDoc}
+     * @param hour {@inheritDoc}
+     * @param minute {@inheritDoc}
+     * @throws HtmlParseException {@inheritDoc}
+     */
+    @Override
+    public void sysEventStayEpilogue(Team winner,
+                                     int hour, int minute)
+            throws HtmlParseException{
+        GameRole role;
+        switch(winner){
+        case VILLAGE: role = GameRole.INNOCENT; break;
+        case WOLF:    role = GameRole.WOLF;     break;
+        case HAMSTER: role = GameRole.HAMSTER;  break;
+        default: throw new IllegalArgumentException();
+        }
+
+        this.currentEvent.addGameRole(role);
+        this.currentEvent.addInteger(hour);
+        this.currentEvent.addInteger(minute);
+
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param content {@inheritDoc}
+     * @param guardByRange {@inheritDoc}
+     * @param guardToRange {@inheritDoc}
+     * @throws HtmlParseException {@inheritDoc}
+     */
+    @Override
+    public void sysEventGuard(DecodedContent content,
+                                SeqRange guardByRange,
+                                SeqRange guardToRange)
+            throws HtmlParseException{
+        DecodedContent avatarName;
+
+        avatarName = this.converter.convert(content, guardByRange);
+        AvatarData guardBy = this.villageData.getAvatarData(avatarName);
+        this.currentEvent.addAvatarData(guardBy);
+
+        avatarName = this.converter.convert(content, guardToRange);
+        AvatarData guardTo = this.villageData.getAvatarData(avatarName);
+        this.currentEvent.addAvatarData(guardTo);
+
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param content {@inheritDoc}
+     * @param judgeByRange {@inheritDoc}
+     * @param judgeToRange {@inheritDoc}
+     * @throws HtmlParseException {@inheritDoc}
+     */
+    @Override
+    public void sysEventJudge(DecodedContent content,
+                                SeqRange judgeByRange,
+                                SeqRange judgeToRange)
+            throws HtmlParseException{
+        DecodedContent avatarName;
+
+        avatarName = this.converter.convert(content, judgeByRange);
+        AvatarData judgeBy = this.villageData.getAvatarData(avatarName);
+        this.currentEvent.addAvatarData(judgeBy);
+
+        avatarName = this.converter.convert(content, judgeToRange);
+        AvatarData judgeTo = this.villageData.getAvatarData(avatarName);
+        this.currentEvent.addAvatarData(judgeTo);
+
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param content {@inheritDoc}
+     * @param avatarRange {@inheritDoc}
+     * @param votes {@inheritDoc}
+     * @throws HtmlParseException {@inheritDoc}
+     */
+    @Override
+    public void sysEventExecution(DecodedContent content,
+                                    SeqRange avatarRange,
+                                    int votes)
+            throws HtmlParseException{
+        DecodedContent avatarName;
+        avatarName = this.converter.convert(content, avatarRange);
+        AvatarData avatar = this.villageData.getAvatarData(avatarName);
+        this.currentEvent.addAvatarData(avatar);
+
+        if(votes > 0){
+            this.currentEvent.addInteger(votes);
+        }
+
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param content {@inheritDoc}
+     * @param avatarRange {@inheritDoc}
+     * @throws HtmlParseException {@inheritDoc}
+     */
+    @Override
+    public void sysEventCheckout(DecodedContent content,
+                                   SeqRange avatarRange)
+            throws HtmlParseException{
+        DecodedContent avatarName;
+        avatarName = this.converter.convert(content, avatarRange);
+        AvatarData avatar = this.villageData.getAvatarData(avatarName);
+        this.currentEvent.addAvatarData(avatar);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param content {@inheritDoc}
+     * @param avatarRange {@inheritDoc}
+     * @throws HtmlParseException {@inheritDoc}
+     */
+    @Override
+    public void sysEventVanish(DecodedContent content,
+                                 SeqRange avatarRange)
+            throws HtmlParseException{
+        DecodedContent avatarName;
+        avatarName = this.converter.convert(content, avatarRange);
+        AvatarData avatar = this.villageData.getAvatarData(avatarName);
+        this.currentEvent.addAvatarData(avatar);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws HtmlParseException {@inheritDoc}
+     */
+    @Override
+    public void endSysEvent() throws HtmlParseException{
+        this.currentPeriod.addTopicData(this.currentEvent);
+        this.currentEvent = null;
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws HtmlParseException {@inheritDoc}
+     */
+    @Override
+    public void endParse() throws HtmlParseException{
+        this.villageData.addPeriodData(this.currentPeriod);
+        this.currentPeriod = null;
+        return;
+    }
+
+}
index 593236b..9237b41 100644 (file)
-/*\r
- * downloader\r
- *\r
- * License : The MIT License\r
- * Copyright(c) 2008 olyutorskii\r
- */\r
-\r
-package jp.sourceforge.jindolf.archiver;\r
-\r
-import java.io.IOException;\r
-import java.io.InputStream;\r
-import java.net.URL;\r
-import java.nio.charset.Charset;\r
-import java.util.LinkedList;\r
-import java.util.List;\r
-import jp.sourceforge.jindolf.corelib.LandDef;\r
-import jp.sourceforge.jindolf.corelib.LandState;\r
-import jp.sourceforge.jindolf.corelib.PeriodType;\r
-import jp.sourceforge.jindolf.parser.DecodeException;\r
-import jp.sourceforge.jindolf.parser.DecodedContent;\r
-import jp.sourceforge.jindolf.parser.HtmlAdapter;\r
-import jp.sourceforge.jindolf.parser.HtmlParseException;\r
-import jp.sourceforge.jindolf.parser.HtmlParser;\r
-import jp.sourceforge.jindolf.parser.PageType;\r
-import jp.sourceforge.jindolf.parser.SeqRange;\r
-\r
-/**\r
- * 人狼HTTPサーバ内のリソース情報を展開する。\r
- */\r
-public final class HttpAccess{\r
-\r
-    /**\r
-     * 隠しコンストラクタ。\r
-     */\r
-    private HttpAccess(){\r
-        throw new Error();\r
-    }\r
-\r
-\r
-    /**\r
-     * 日一覧ページ(エピローグの翌日)のURLを得る。\r
-     * @param landDef 国指定\r
-     * @param vid 村番号\r
-     * @return 一覧ページへのURL\r
-     * @throws IOException 入力エラー\r
-     */\r
-    public static URL getPeriodListURL(LandDef landDef, int vid)\r
-            throws IOException{\r
-        StringBuilder urlText = new StringBuilder();\r
-\r
-        urlText.append(landDef.getCgiURI().toASCIIString());\r
-        urlText.append('?').append("vid=").append(vid);\r
-        if(landDef.getLandState() == LandState.ACTIVE){\r
-            urlText.append('&').append("meslog=");\r
-        }\r
-\r
-        URL result = new URL(urlText.toString());\r
-\r
-        return result;\r
-    }\r
-\r
-    /**\r
-     * 日ページのロード元情報一覧を得る。\r
-     * @param landDef 国指定\r
-     * @param vid 村番号\r
-     * @return ロード元情報一覧\r
-     * @throws DecodeException デコードエラー\r
-     * @throws HtmlParseException パースエラー\r
-     * @throws IOException 入力エラー\r
-     */\r
-    public static List<PeriodResource> loadResourceList(LandDef landDef,\r
-                                                          int vid)\r
-            throws DecodeException,\r
-                   HtmlParseException,\r
-                   IOException {\r
-        URL url = getPeriodListURL(landDef, vid);\r
-\r
-        Charset charset = landDef.getEncoding();\r
-        InputStream istream = url.openStream();\r
-        DecodedContent content = Builder.contentFromStream(charset, istream);\r
-        istream.close();\r
-\r
-        HtmlParser parser = new HtmlParser();\r
-        PeriodListHandler handler = new PeriodListHandler(landDef, vid);\r
-        parser.setBasicHandler(handler);\r
-        parser.setTalkHandler(handler);\r
-        parser.setSysEventHandler(handler);\r
-        parser.parseAutomatic(content);\r
-\r
-        List<PeriodResource> result = handler.getResourceList();\r
-\r
-        return result;\r
-    }\r
-\r
-    /**\r
-     * 日一覧パース用ハンドラ。\r
-     */\r
-    public static class PeriodListHandler extends HtmlAdapter{\r
-\r
-        private final LandDef landDef;\r
-        private final int vid;\r
-\r
-        private List<PeriodResource> resourceList = null;\r
-\r
-        private int progressDays;\r
-        private boolean hasDone;\r
-\r
-        /**\r
-         * コンストラクタ。\r
-         * @param landDef 国指定\r
-         * @param vid 村番号\r
-         */\r
-        public PeriodListHandler(LandDef landDef, int vid){\r
-            super();\r
-            this.landDef = landDef;\r
-            this.vid = vid;\r
-            return;\r
-        }\r
-\r
-        /**\r
-         * 日ページのURL文字列を生成する。\r
-         * @param type 日種類\r
-         * @param day 日にち\r
-         * @return URL文字列\r
-         */\r
-        public String getURL(PeriodType type, int day){\r
-            String base = this.landDef.getCgiURI().toASCIIString();\r
-            base += "?vid=" + this.vid;\r
-\r
-            if(this.landDef.getLandId().equals("wolfg")){\r
-                base += "&meslog=";\r
-                String dnum = "000" + (day - 1);\r
-                dnum = dnum.substring(dnum.length() - 3);\r
-                switch(type){\r
-                case PROLOGUE:\r
-                    base += "000_ready";\r
-                    break;\r
-                case PROGRESS:\r
-                    base += dnum;\r
-                    base += "_progress";\r
-                    break;\r
-                case EPILOGUE:\r
-                    base += dnum;\r
-                    base += "_party";\r
-                    break;\r
-                default:\r
-                    assert false;\r
-                    return null;\r
-                }\r
-            }else{\r
-                base += "&meslog=" + this.vid + "_";\r
-                switch(type){\r
-                case PROLOGUE:\r
-                    base += "ready_0";\r
-                    break;\r
-                case PROGRESS:\r
-                    base += "progress_" + (day - 1);\r
-                    break;\r
-                case EPILOGUE:\r
-                    base += "party_" + (day - 1);\r
-                    break;\r
-                default:\r
-                    return null;\r
-                }\r
-            }\r
-\r
-            base += "&mes=all";\r
-\r
-            return base;\r
-        }\r
-\r
-        /**\r
-         * PeriodResource一覧を得る。\r
-         * @return PeriodResource一覧\r
-         */\r
-        public List<PeriodResource> getResourceList(){\r
-            return this.resourceList;\r
-        }\r
-\r
-        /**\r
-         * {@inheritDoc}\r
-         * @param content {@inheritDoc}\r
-         * @throws HtmlParseException {@inheritDoc}\r
-         */\r
-        @Override\r
-        public void startParse(DecodedContent content)\r
-                throws HtmlParseException{\r
-            this.resourceList = new LinkedList<PeriodResource>();\r
-            this.progressDays = 0;\r
-            this.hasDone = false;\r
-            return;\r
-        }\r
-\r
-        /**\r
-         * {@inheritDoc}\r
-         * @param type {@inheritDoc}\r
-         * @throws HtmlParseException {@inheritDoc}\r
-         */\r
-        @Override\r
-        public void pageType(PageType type) throws HtmlParseException{\r
-            if(type != PageType.PERIOD_PAGE) throw new HtmlParseException();\r
-            return;\r
-        }\r
-\r
-        /**\r
-         * {@inheritDoc}\r
-         * @param content {@inheritDoc}\r
-         * @param anchorRange {@inheritDoc}\r
-         * @param periodType {@inheritDoc}\r
-         * @param day {@inheritDoc}\r
-         * @throws HtmlParseException {@inheritDoc}\r
-         */\r
-        @Override\r
-        public void periodLink(DecodedContent content,\r
-                                SeqRange anchorRange,\r
-                                PeriodType periodType,\r
-                                int day )\r
-                throws HtmlParseException{\r
-            if(periodType == null){\r
-                this.hasDone = true;\r
-            }else if(periodType == PeriodType.PROGRESS){\r
-                this.progressDays = day;\r
-            }\r
-            return;\r
-        }\r
-\r
-        /**\r
-         * {@inheritDoc}\r
-         * @throws HtmlParseException {@inheritDoc}\r
-         */\r
-        @Override\r
-        public void endParse() throws HtmlParseException{\r
-            if( ! this.hasDone ) throw new HtmlParseException();\r
-\r
-            PeriodResource resource;\r
-\r
-            String prologueURI = getURL(PeriodType.PROLOGUE, 0);\r
-            resource = new PeriodResource(this.landDef,\r
-                                          this.vid,\r
-                                          PeriodType.PROLOGUE,\r
-                                          0,\r
-                                          prologueURI,\r
-                                          0L,\r
-                                          null);\r
-            this.resourceList.add(resource);\r
-\r
-            for(int day = 1; day <= this.progressDays; day++){\r
-                String progressURI = getURL(PeriodType.PROGRESS, day);\r
-                resource = new PeriodResource(this.landDef,\r
-                                              this.vid,\r
-                                              PeriodType.PROGRESS,\r
-                                              day,\r
-                                              progressURI,\r
-                                              0L,\r
-                                              null);\r
-                this.resourceList.add(resource);\r
-            }\r
-\r
-            String epilogueURI = getURL(PeriodType.EPILOGUE,\r
-                                        this.progressDays + 1);\r
-            resource = new PeriodResource(this.landDef,\r
-                                          this.vid,\r
-                                          PeriodType.EPILOGUE,\r
-                                          this.progressDays + 1,\r
-                                          epilogueURI,\r
-                                          0L,\r
-                                          null);\r
-            this.resourceList.add(resource);\r
-\r
-            return;\r
-        }\r
-\r
-    }\r
-\r
-}\r
+/*
+ * downloader
+ *
+ * License : The MIT License
+ * Copyright(c) 2008 olyutorskii
+ */
+
+package jp.sourceforge.jindolf.archiver;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.nio.charset.Charset;
+import java.util.LinkedList;
+import java.util.List;
+import jp.sourceforge.jindolf.corelib.LandDef;
+import jp.sourceforge.jindolf.corelib.LandState;
+import jp.sourceforge.jindolf.corelib.PeriodType;
+import jp.sourceforge.jindolf.parser.DecodeException;
+import jp.sourceforge.jindolf.parser.DecodedContent;
+import jp.sourceforge.jindolf.parser.HtmlAdapter;
+import jp.sourceforge.jindolf.parser.HtmlParseException;
+import jp.sourceforge.jindolf.parser.HtmlParser;
+import jp.sourceforge.jindolf.parser.PageType;
+import jp.sourceforge.jindolf.parser.SeqRange;
+
+/**
+ * 人狼HTTPサーバ内のリソース情報を展開する。
+ */
+public final class HttpAccess{
+
+    /**
+     * 隠しコンストラクタ。
+     */
+    private HttpAccess(){
+        throw new Error();
+    }
+
+
+    /**
+     * 日一覧ページ(エピローグの翌日)のURLを得る。
+     * @param landDef 国指定
+     * @param vid 村番号
+     * @return 一覧ページへのURL
+     * @throws IOException 入力エラー
+     */
+    public static URL getPeriodListURL(LandDef landDef, int vid)
+            throws IOException{
+        StringBuilder urlText = new StringBuilder();
+
+        urlText.append(landDef.getCgiURI().toASCIIString());
+        urlText.append('?').append("vid=").append(vid);
+        if(landDef.getLandState() == LandState.ACTIVE){
+            urlText.append('&').append("meslog=");
+        }
+
+        URL result = new URL(urlText.toString());
+
+        return result;
+    }
+
+    /**
+     * 日ページのロード元情報一覧を得る。
+     * @param landDef 国指定
+     * @param vid 村番号
+     * @return ロード元情報一覧
+     * @throws DecodeException デコードエラー
+     * @throws HtmlParseException パースエラー
+     * @throws IOException 入力エラー
+     */
+    public static List<PeriodResource> loadResourceList(LandDef landDef,
+                                                          int vid)
+            throws DecodeException,
+                   HtmlParseException,
+                   IOException {
+        URL url = getPeriodListURL(landDef, vid);
+
+        Charset charset = landDef.getEncoding();
+        InputStream istream = url.openStream();
+        DecodedContent content = Builder.contentFromStream(charset, istream);
+        istream.close();
+
+        HtmlParser parser = new HtmlParser();
+        PeriodListHandler handler = new PeriodListHandler(landDef, vid);
+        parser.setBasicHandler(handler);
+        parser.setTalkHandler(handler);
+        parser.setSysEventHandler(handler);
+        parser.parseAutomatic(content);
+
+        List<PeriodResource> result = handler.getResourceList();
+
+        return result;
+    }
+
+    /**
+     * 日一覧パース用ハンドラ。
+     */
+    public static class PeriodListHandler extends HtmlAdapter{
+
+        private final LandDef landDef;
+        private final int vid;
+
+        private List<PeriodResource> resourceList = null;
+
+        private int progressDays;
+        private boolean hasDone;
+
+        /**
+         * コンストラクタ。
+         * @param landDef 国指定
+         * @param vid 村番号
+         */
+        public PeriodListHandler(LandDef landDef, int vid){
+            super();
+            this.landDef = landDef;
+            this.vid = vid;
+            return;
+        }
+
+        /**
+         * 日ページのURL文字列を生成する。
+         * @param type 日種類
+         * @param day 日にち
+         * @return URL文字列
+         */
+        public String getURL(PeriodType type, int day){
+            String base = this.landDef.getCgiURI().toASCIIString();
+            base += "?vid=" + this.vid;
+
+            if(this.landDef.getLandId().equals("wolfg")){
+                base += "&meslog=";
+                String dnum = "000" + (day - 1);
+                dnum = dnum.substring(dnum.length() - 3);
+                switch(type){
+                case PROLOGUE:
+                    base += "000_ready";
+                    break;
+                case PROGRESS:
+                    base += dnum;
+                    base += "_progress";
+                    break;
+                case EPILOGUE:
+                    base += dnum;
+                    base += "_party";
+                    break;
+                default:
+                    assert false;
+                    return null;
+                }
+            }else{
+                base += "&meslog=" + this.vid + "_";
+                switch(type){
+                case PROLOGUE:
+                    base += "ready_0";
+                    break;
+                case PROGRESS:
+                    base += "progress_" + (day - 1);
+                    break;
+                case EPILOGUE:
+                    base += "party_" + (day - 1);
+                    break;
+                default:
+                    return null;
+                }
+            }
+
+            base += "&mes=all";
+
+            return base;
+        }
+
+        /**
+         * PeriodResource一覧を得る。
+         * @return PeriodResource一覧
+         */
+        public List<PeriodResource> getResourceList(){
+            return this.resourceList;
+        }
+
+        /**
+         * {@inheritDoc}
+         * @param content {@inheritDoc}
+         * @throws HtmlParseException {@inheritDoc}
+         */
+        @Override
+        public void startParse(DecodedContent content)
+                throws HtmlParseException{
+            this.resourceList = new LinkedList<PeriodResource>();
+            this.progressDays = 0;
+            this.hasDone = false;
+            return;
+        }
+
+        /**
+         * {@inheritDoc}
+         * @param type {@inheritDoc}
+         * @throws HtmlParseException {@inheritDoc}
+         */
+        @Override
+        public void pageType(PageType type) throws HtmlParseException{
+            if(type != PageType.PERIOD_PAGE) throw new HtmlParseException();
+            return;
+        }
+
+        /**
+         * {@inheritDoc}
+         * @param content {@inheritDoc}
+         * @param anchorRange {@inheritDoc}
+         * @param periodType {@inheritDoc}
+         * @param day {@inheritDoc}
+         * @throws HtmlParseException {@inheritDoc}
+         */
+        @Override
+        public void periodLink(DecodedContent content,
+                                SeqRange anchorRange,
+                                PeriodType periodType,
+                                int day )
+                throws HtmlParseException{
+            if(periodType == null){
+                this.hasDone = true;
+            }else if(periodType == PeriodType.PROGRESS){
+                this.progressDays = day;
+            }
+            return;
+        }
+
+        /**
+         * {@inheritDoc}
+         * @throws HtmlParseException {@inheritDoc}
+         */
+        @Override
+        public void endParse() throws HtmlParseException{
+            if( ! this.hasDone ) throw new HtmlParseException();
+
+            PeriodResource resource;
+
+            String prologueURI = getURL(PeriodType.PROLOGUE, 0);
+            resource = new PeriodResource(this.landDef,
+                                          this.vid,
+                                          PeriodType.PROLOGUE,
+                                          0,
+                                          prologueURI,
+                                          0L,
+                                          null);
+            this.resourceList.add(resource);
+
+            for(int day = 1; day <= this.progressDays; day++){
+                String progressURI = getURL(PeriodType.PROGRESS, day);
+                resource = new PeriodResource(this.landDef,
+                                              this.vid,
+                                              PeriodType.PROGRESS,
+                                              day,
+                                              progressURI,
+                                              0L,
+                                              null);
+                this.resourceList.add(resource);
+            }
+
+            String epilogueURI = getURL(PeriodType.EPILOGUE,
+                                        this.progressDays + 1);
+            resource = new PeriodResource(this.landDef,
+                                          this.vid,
+                                          PeriodType.EPILOGUE,
+                                          this.progressDays + 1,
+                                          epilogueURI,
+                                          0L,
+                                          null);
+            this.resourceList.add(resource);
+
+            return;
+        }
+
+    }
+
+}
index a62dc52..41542bf 100644 (file)
-/*\r
- * main entry\r
- *\r
- * License : The MIT License\r
- * Copyright(c) 2008 olyutorskii\r
- */\r
-\r
-package jp.sourceforge.jindolf.archiver;\r
-\r
-import java.io.BufferedOutputStream;\r
-import java.io.BufferedWriter;\r
-import java.io.File;\r
-import java.io.FileOutputStream;\r
-import java.io.IOException;\r
-import java.io.InputStream;\r
-import java.io.OutputStream;\r
-import java.io.OutputStreamWriter;\r
-import java.io.Writer;\r
-import java.text.MessageFormat;\r
-import java.util.List;\r
-import java.util.Properties;\r
-import javax.xml.parsers.DocumentBuilder;\r
-import javax.xml.parsers.DocumentBuilderFactory;\r
-import javax.xml.parsers.ParserConfigurationException;\r
-import jp.sourceforge.jindolf.corelib.LandDef;\r
-import jp.sourceforge.jindolf.parser.DecodeException;\r
-import jp.sourceforge.jindolf.parser.HtmlParseException;\r
-import org.xml.sax.SAXException;\r
-\r
-/**\r
- * メインエントリ。\r
- */\r
-public final class JinArchiver{\r
-\r
-    /** Generator. */\r
-    public static final String GENERATOR;\r
-\r
-    /** このClass。 */\r
-    private static final Class<?> SELF_KLASS;\r
-    /** このPackage。 */\r
-    private static final Package  SELF_PACKAGE;\r
-    /** タイトル。 */\r
-    private static final String TITLE;\r
-    /** バージョン。 */\r
-    private static final String VERSION;\r
-\r
-    private static final List<LandDef> LANDDEF_LIST;\r
-\r
-    /** バージョン定義リソース。 */\r
-    private static final String RES_VERDEF = "resources/version.properties";\r
-\r
-    static{\r
-        SELF_KLASS   = JinArchiver.class;\r
-        SELF_PACKAGE = SELF_KLASS.getPackage();\r
-\r
-        Properties verProp = loadVersionDefinition(SELF_KLASS);\r
-        TITLE   = getPackageInfo(verProp, "pkg-title.",   "Unknown");\r
-        VERSION = getPackageInfo(verProp, "pkg-version.", "0");\r
-        GENERATOR = TITLE + " " + VERSION;\r
-\r
-        DocumentBuilderFactory factory =\r
-                DocumentBuilderFactory.newInstance();\r
-        try{\r
-            DocumentBuilder builder = factory.newDocumentBuilder();\r
-            LANDDEF_LIST = LandDef.buildLandDefList(builder);\r
-        }catch(ParserConfigurationException e){\r
-            throw new ExceptionInInitializerError(e);\r
-        }catch(IOException e){\r
-            throw new ExceptionInInitializerError(e);\r
-        }catch(SAXException e){\r
-            throw new ExceptionInInitializerError(e);\r
-        }\r
-    }\r
-\r
-\r
-    /**\r
-     * 隠しコンストラクタ。\r
-     */\r
-    private JinArchiver(){\r
-        throw new Error();\r
-    }\r
-\r
-\r
-    /**\r
-     * リソース上のパッケージ定義プロパティをロードする。\r
-     * MANIFEST.MFが参照できない実行環境での代替品。\r
-     * @param klass パッケージを構成する任意のクラス\r
-     * @return プロパティ\r
-     */\r
-    private static Properties loadVersionDefinition(Class klass){\r
-        Properties result = new Properties();\r
-\r
-        InputStream istream = klass.getResourceAsStream(RES_VERDEF);\r
-        try{\r
-            result.load(istream);\r
-        }catch(IOException e){\r
-            return result;\r
-        }finally{\r
-            try{\r
-                istream.close();\r
-            }catch(IOException e){\r
-                return result;\r
-            }\r
-        }\r
-\r
-        return result;\r
-    }\r
-\r
-    /**\r
-     * リソース上のプロパティから\r
-     * このクラスのパッケージのパッケージ情報を取得する。\r
-     * MANIFEST.MFが参照できない実行環境での代替品。\r
-     * @param prop プロパティ\r
-     * @param prefix 接頭辞\r
-     * @param defValue 見つからなかった場合のデフォルト値\r
-     * @return パッケージ情報\r
-     */\r
-    private static String getPackageInfo(Properties prop,\r
-                                          String prefix,\r
-                                          String defValue){\r
-        return getPackageInfo(prop, SELF_PACKAGE, prefix, defValue);\r
-    }\r
-\r
-    /**\r
-     * リソース上のプロパティからパッケージ情報を取得する。\r
-     * MANIFEST.MFが参照できない実行環境での代替品。\r
-     * @param prop プロパティ\r
-     * @param pkg 任意のパッケージ\r
-     * @param prefix 接頭辞\r
-     * @param defValue デフォルト値\r
-     * @return 見つからなかった場合のパッケージ情報\r
-     */\r
-    private static String getPackageInfo(Properties prop,\r
-                                          Package pkg,\r
-                                          String prefix,\r
-                                          String defValue){\r
-        String propName = prefix + pkg.getName();\r
-        String result = prop.getProperty(propName, defValue);\r
-        return result;\r
-    }\r
-\r
-    /**\r
-     * System.err.println()のWrapper。\r
-     * @param text 出力テキスト\r
-     */\r
-    private static void errprintln(CharSequence text){\r
-        System.err.println(text);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * プログラムの終了。\r
-     * @param code プロセスコード。\r
-     */\r
-    private static void exit(int code){\r
-        System.exit(code);\r
-        assert false;\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 国IDから国情報を得る。\r
-     * @param landId 国ID\r
-     * @return 国情報\r
-     */\r
-    public static LandDef getLandDef(String landId){\r
-        for(LandDef landDef : LANDDEF_LIST){\r
-            if(landDef.getLandId().equals(landId)) return landDef;\r
-        }\r
-        return null;\r
-    }\r
-\r
-    /**\r
-     * ヘルプメッセージ出力。\r
-     */\r
-    private static void helpMessage(){\r
-        errprintln(\r
-                "\n" + GENERATOR + " 人狼BBS アーカイブ作成ツール\n\n"\r
-                +"-h, -help, -?\n\tヘルプメッセージ\n"\r
-                +"-land 国識別子\n"\r
-                +"-vid 村番号\n"\r
-                +"-outdir 出力ディレクトリ\n"\r
-                +"-stdout\n\t標準出力へ出力\n\n"\r
-                +"※ -outdir と -stdout は排他指定\n"\r
-                );\r
-        StringBuilder landList = new StringBuilder();\r
-        for(LandDef landDef : LANDDEF_LIST){\r
-            landList.append(landDef.getLandId()).append(' ');\r
-        }\r
-        errprintln("利用可能な国識別子は " + landList + "\n");\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * オプション文字列を解析する。\r
-     * @param args オプション文字列\r
-     */\r
-    private static void parseOption(String[] args){\r
-        if(args.length <= 0){\r
-            helpMessage();\r
-            exit(0);\r
-            return;\r
-        }\r
-\r
-        LandDef landDef = null;\r
-        int vid = -1;\r
-        String outdir = null;\r
-        boolean stdout = false;\r
-\r
-        for(int pos = 0; pos < args.length; pos++){\r
-            String arg = args[pos];\r
-\r
-            if( ! arg.startsWith("-") ){\r
-                errprintln("不正なオプションです。 " + arg);\r
-                exit(1);\r
-                return;\r
-            }\r
-\r
-            if(arg.equals("-h") || arg.equals("-help") || arg.equals("-?")){\r
-                helpMessage();\r
-                exit(0);\r
-                return;\r
-            }\r
-\r
-            if(arg.equals("-stdout")){\r
-                stdout = true;\r
-                outdir = null;\r
-                continue;\r
-            }\r
-\r
-            if(++pos >= args.length){\r
-                errprintln(\r
-                        "オプション " + arg + " に引数がありません。");\r
-                exit(1);\r
-                return;\r
-            }\r
-\r
-            String val = args[pos];\r
-            if(arg.equals("-land")){\r
-                landDef = getLandDef(val);\r
-                if(landDef == null){\r
-                    errprintln("不正な国識別子です。 " + val);\r
-                    exit(1);\r
-                    return;\r
-                }\r
-            }else if(arg.equals("-vid")){\r
-                vid = Integer.parseInt(val);\r
-                if(vid < 0){\r
-                    errprintln("不正な村番号です。 " + vid);\r
-                    exit(1);\r
-                    return;\r
-                }\r
-            }else if(arg.equals("-outdir")){\r
-                outdir = val;\r
-                stdout = false;\r
-            }else{\r
-                errprintln("不正なオプションです。 " + arg);\r
-                exit(1);\r
-                return;\r
-            }\r
-        }\r
-\r
-        if(landDef == null){\r
-            errprintln(\r
-                    "-land オプションで国識別子を指定してください。");\r
-            exit(1);\r
-            return;\r
-        }\r
-\r
-        if(vid < 0){\r
-            errprintln(\r
-                    "-vid オプションで村番号を指定してください。");\r
-            exit(1);\r
-            return;\r
-        }\r
-\r
-        if(   (outdir == null && stdout == false)\r
-           || (outdir != null && stdout == true)  ){\r
-            errprintln(\r
-                    "-outdir か -stdout のどちらか一方を指定してください。");\r
-            exit(1);\r
-            return;\r
-        }\r
-\r
-        Writer writer;\r
-        if(outdir != null){\r
-            writer = getFileWriter(outdir, landDef, vid);\r
-        }else{\r
-            writer = getStdOutWriter();\r
-        }\r
-\r
-        writer = ValidateTask.wrapValidator(writer);\r
-\r
-        try{\r
-            dump(writer, landDef, vid);\r
-        }catch(IOException e){\r
-            abortWithException(e);\r
-        }catch(DecodeException e){\r
-            abortWithException(e);\r
-        }catch(HtmlParseException e){\r
-            abortWithException(e);\r
-        }\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 例外によるアプリ終了。\r
-     * @param e 例外\r
-     */\r
-    private static void abortWithException(Exception e){\r
-        e.printStackTrace(System.err);\r
-        errprintln("処理を続行できません。");\r
-        exit(1);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 主処理。人狼サーバからXHTMLを読み込み。XMLで出力。\r
-     * @param writer 出力先\r
-     * @param landDef 国情報\r
-     * @param vid 村番号\r
-     * @throws IOException 入出力エラー\r
-     * @throws DecodeException デコードエラー\r
-     * @throws HtmlParseException パースエラー\r
-     */\r
-    public static void dump(Writer writer, LandDef landDef, int vid)\r
-            throws IOException, DecodeException, HtmlParseException{\r
-        List<PeriodResource> resourceList =\r
-                HttpAccess.loadResourceList(landDef, vid);\r
-        VillageData village = new VillageData(resourceList);\r
-\r
-        Builder.fillVillageData(village);\r
-        XmlUtils.dumpVillageData(writer, village);\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 標準出力への出力先を得る。\r
-     * @return 出力先\r
-     */\r
-    public static Writer getStdOutWriter(){\r
-        OutputStream ostream;\r
-        ostream = new BufferedOutputStream(System.out);\r
-        Writer writer;\r
-        try{\r
-            writer = new OutputStreamWriter(ostream, "UTF-8");\r
-            writer = new BufferedWriter(writer, 4 * 1024);\r
-        }catch(IOException e){\r
-            errprintln(\r
-                    "標準出力に書き込めません。");\r
-            exit(1);\r
-            return null;\r
-        }\r
-        return writer;\r
-    }\r
-\r
-    /**\r
-     * ローカルファイルへの出力先を得る。\r
-     * @param outdir 出力ディレクトリ\r
-     * @param landDef 国情報\r
-     * @param vid 村番号\r
-     * @return 出力先\r
-     */\r
-    public static Writer getFileWriter(String outdir,\r
-                                         LandDef landDef,\r
-                                         int vid ){\r
-            File outFile = new File(outdir);\r
-            if( ! outFile.exists() ){\r
-                errprintln(\r
-                        outdir + " が存在しません。");\r
-                exit(1);\r
-                return null;\r
-            }\r
-            if( ! outFile.isDirectory() ){\r
-                errprintln(\r
-                        outdir + " はディレクトリではありません。");\r
-                exit(1);\r
-                return null;\r
-            }\r
-            if( ! outFile.canWrite() ){\r
-                errprintln(\r
-                        outdir + " に書き込めません。");\r
-                exit(1);\r
-                return null;\r
-            }\r
-            String fname = MessageFormat.format(\r
-                "jin_{0}_{1,number,#00000}.xml", landDef.getLandId(), vid);\r
-            File xmlFile = new File(outFile, fname);\r
-            boolean created;\r
-            try{\r
-                created = xmlFile.createNewFile();\r
-            }catch(IOException e){\r
-                errprintln(\r
-                        xmlFile.getName() + " が作成できません。");\r
-                exit(1);\r
-                return null;\r
-            }\r
-            if( ! created ){\r
-                errprintln(\r
-                        fname + " が既に" + outdir + "に存在します。");\r
-                exit(1);\r
-                return null;\r
-            }\r
-            /* JRE 1.6 only\r
-            xmlFile.setReadable(true);\r
-            xmlFile.setWritable(true);\r
-            xmlFile.setExecutable(false, false);\r
-            */\r
-            Writer writer;\r
-            try{\r
-                OutputStream ostream;\r
-                ostream = new FileOutputStream(xmlFile);\r
-                ostream = new BufferedOutputStream(ostream, 4 * 1024);\r
-                writer = new OutputStreamWriter(ostream, "UTF-8");\r
-                writer = new BufferedWriter(writer, 4 * 1024);\r
-            }catch(IOException e){\r
-                errprintln(\r
-                        xmlFile.getName() + " に書き込めません。");\r
-                exit(1);\r
-                return null;\r
-            }\r
-\r
-            return writer;\r
-    }\r
-\r
-    /**\r
-     * スタートアップエントリ。\r
-     * @param args 引数\r
-     */\r
-    public static void main(String[] args){\r
-        parseOption(args);\r
-        exit(0);\r
-        return;\r
-    }\r
-\r
-}\r
+/*
+ * main entry
+ *
+ * License : The MIT License
+ * Copyright(c) 2008 olyutorskii
+ */
+
+package jp.sourceforge.jindolf.archiver;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.text.MessageFormat;
+import java.util.List;
+import java.util.Properties;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import jp.sourceforge.jindolf.corelib.LandDef;
+import jp.sourceforge.jindolf.parser.DecodeException;
+import jp.sourceforge.jindolf.parser.HtmlParseException;
+import org.xml.sax.SAXException;
+
+/**
+ * メインエントリ。
+ */
+public final class JinArchiver{
+
+    /** Generator. */
+    public static final String GENERATOR;
+
+    /** このClass。 */
+    private static final Class<?> SELF_KLASS;
+    /** このPackage。 */
+    private static final Package  SELF_PACKAGE;
+    /** タイトル。 */
+    private static final String TITLE;
+    /** バージョン。 */
+    private static final String VERSION;
+
+    private static final List<LandDef> LANDDEF_LIST;
+
+    /** バージョン定義リソース。 */
+    private static final String RES_VERDEF = "resources/version.properties";
+
+    static{
+        SELF_KLASS   = JinArchiver.class;
+        SELF_PACKAGE = SELF_KLASS.getPackage();
+
+        Properties verProp = loadVersionDefinition(SELF_KLASS);
+        TITLE   = getPackageInfo(verProp, "pkg-title.",   "Unknown");
+        VERSION = getPackageInfo(verProp, "pkg-version.", "0");
+        GENERATOR = TITLE + " " + VERSION;
+
+        DocumentBuilderFactory factory =
+                DocumentBuilderFactory.newInstance();
+        try{
+            DocumentBuilder builder = factory.newDocumentBuilder();
+            LANDDEF_LIST = LandDef.buildLandDefList(builder);
+        }catch(ParserConfigurationException e){
+            throw new ExceptionInInitializerError(e);
+        }catch(IOException e){
+            throw new ExceptionInInitializerError(e);
+        }catch(SAXException e){
+            throw new ExceptionInInitializerError(e);
+        }
+    }
+
+
+    /**
+     * 隠しコンストラクタ。
+     */
+    private JinArchiver(){
+        throw new Error();
+    }
+
+
+    /**
+     * リソース上のパッケージ定義プロパティをロードする。
+     * MANIFEST.MFが参照できない実行環境での代替品。
+     * @param klass パッケージを構成する任意のクラス
+     * @return プロパティ
+     */
+    private static Properties loadVersionDefinition(Class klass){
+        Properties result = new Properties();
+
+        InputStream istream = klass.getResourceAsStream(RES_VERDEF);
+        try{
+            result.load(istream);
+        }catch(IOException e){
+            return result;
+        }finally{
+            try{
+                istream.close();
+            }catch(IOException e){
+                return result;
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * リソース上のプロパティから
+     * このクラスのパッケージのパッケージ情報を取得する。
+     * MANIFEST.MFが参照できない実行環境での代替品。
+     * @param prop プロパティ
+     * @param prefix 接頭辞
+     * @param defValue 見つからなかった場合のデフォルト値
+     * @return パッケージ情報
+     */
+    private static String getPackageInfo(Properties prop,
+                                          String prefix,
+                                          String defValue){
+        return getPackageInfo(prop, SELF_PACKAGE, prefix, defValue);
+    }
+
+    /**
+     * リソース上のプロパティからパッケージ情報を取得する。
+     * MANIFEST.MFが参照できない実行環境での代替品。
+     * @param prop プロパティ
+     * @param pkg 任意のパッケージ
+     * @param prefix 接頭辞
+     * @param defValue デフォルト値
+     * @return 見つからなかった場合のパッケージ情報
+     */
+    private static String getPackageInfo(Properties prop,
+                                          Package pkg,
+                                          String prefix,
+                                          String defValue){
+        String propName = prefix + pkg.getName();
+        String result = prop.getProperty(propName, defValue);
+        return result;
+    }
+
+    /**
+     * System.err.println()のWrapper。
+     * @param text 出力テキスト
+     */
+    private static void errprintln(CharSequence text){
+        System.err.println(text);
+        return;
+    }
+
+    /**
+     * プログラムの終了。
+     * @param code プロセスコード。
+     */
+    private static void exit(int code){
+        System.exit(code);
+        assert false;
+        return;
+    }
+
+    /**
+     * 国IDから国情報を得る。
+     * @param landId 国ID
+     * @return 国情報
+     */
+    public static LandDef getLandDef(String landId){
+        for(LandDef landDef : LANDDEF_LIST){
+            if(landDef.getLandId().equals(landId)) return landDef;
+        }
+        return null;
+    }
+
+    /**
+     * ヘルプメッセージ出力。
+     */
+    private static void helpMessage(){
+        errprintln(
+                "\n" + GENERATOR + " 人狼BBS アーカイブ作成ツール\n\n"
+                +"-h, -help, -?\n\tヘルプメッセージ\n"
+                +"-land 国識別子\n"
+                +"-vid 村番号\n"
+                +"-outdir 出力ディレクトリ\n"
+                +"-stdout\n\t標準出力へ出力\n\n"
+                +"※ -outdir と -stdout は排他指定\n"
+                );
+        StringBuilder landList = new StringBuilder();
+        for(LandDef landDef : LANDDEF_LIST){
+            landList.append(landDef.getLandId()).append(' ');
+        }
+        errprintln("利用可能な国識別子は " + landList + "\n");
+
+        return;
+    }
+
+    /**
+     * オプション文字列を解析する。
+     * @param args オプション文字列
+     */
+    private static void parseOption(String[] args){
+        if(args.length <= 0){
+            helpMessage();
+            exit(0);
+            return;
+        }
+
+        LandDef landDef = null;
+        int vid = -1;
+        String outdir = null;
+        boolean stdout = false;
+
+        for(int pos = 0; pos < args.length; pos++){
+            String arg = args[pos];
+
+            if( ! arg.startsWith("-") ){
+                errprintln("不正なオプションです。 " + arg);
+                exit(1);
+                return;
+            }
+
+            if(arg.equals("-h") || arg.equals("-help") || arg.equals("-?")){
+                helpMessage();
+                exit(0);
+                return;
+            }
+
+            if(arg.equals("-stdout")){
+                stdout = true;
+                outdir = null;
+                continue;
+            }
+
+            if(++pos >= args.length){
+                errprintln(
+                        "オプション " + arg + " に引数がありません。");
+                exit(1);
+                return;
+            }
+
+            String val = args[pos];
+            if(arg.equals("-land")){
+                landDef = getLandDef(val);
+                if(landDef == null){
+                    errprintln("不正な国識別子です。 " + val);
+                    exit(1);
+                    return;
+                }
+            }else if(arg.equals("-vid")){
+                vid = Integer.parseInt(val);
+                if(vid < 0){
+                    errprintln("不正な村番号です。 " + vid);
+                    exit(1);
+                    return;
+                }
+            }else if(arg.equals("-outdir")){
+                outdir = val;
+                stdout = false;
+            }else{
+                errprintln("不正なオプションです。 " + arg);
+                exit(1);
+                return;
+            }
+        }
+
+        if(landDef == null){
+            errprintln(
+                    "-land オプションで国識別子を指定してください。");
+            exit(1);
+            return;
+        }
+
+        if(vid < 0){
+            errprintln(
+                    "-vid オプションで村番号を指定してください。");
+            exit(1);
+            return;
+        }
+
+        if(   (outdir == null && stdout == false)
+           || (outdir != null && stdout == true)  ){
+            errprintln(
+                    "-outdir か -stdout のどちらか一方を指定してください。");
+            exit(1);
+            return;
+        }
+
+        Writer writer;
+        if(outdir != null){
+            writer = getFileWriter(outdir, landDef, vid);
+        }else{
+            writer = getStdOutWriter();
+        }
+
+        writer = ValidateTask.wrapValidator(writer);
+
+        try{
+            dump(writer, landDef, vid);
+        }catch(IOException e){
+            abortWithException(e);
+        }catch(DecodeException e){
+            abortWithException(e);
+        }catch(HtmlParseException e){
+            abortWithException(e);
+        }
+
+        return;
+    }
+
+    /**
+     * 例外によるアプリ終了。
+     * @param e 例外
+     */
+    private static void abortWithException(Exception e){
+        e.printStackTrace(System.err);
+        errprintln("処理を続行できません。");
+        exit(1);
+        return;
+    }
+
+    /**
+     * 主処理。人狼サーバからXHTMLを読み込み。XMLで出力。
+     * @param writer 出力先
+     * @param landDef 国情報
+     * @param vid 村番号
+     * @throws IOException 入出力エラー
+     * @throws DecodeException デコードエラー
+     * @throws HtmlParseException パースエラー
+     */
+    public static void dump(Writer writer, LandDef landDef, int vid)
+            throws IOException, DecodeException, HtmlParseException{
+        List<PeriodResource> resourceList =
+                HttpAccess.loadResourceList(landDef, vid);
+        VillageData village = new VillageData(resourceList);
+
+        Builder.fillVillageData(village);
+        XmlUtils.dumpVillageData(writer, village);
+
+        return;
+    }
+
+    /**
+     * 標準出力への出力先を得る。
+     * @return 出力先
+     */
+    public static Writer getStdOutWriter(){
+        OutputStream ostream;
+        ostream = new BufferedOutputStream(System.out);
+        Writer writer;
+        try{
+            writer = new OutputStreamWriter(ostream, "UTF-8");
+            writer = new BufferedWriter(writer, 4 * 1024);
+        }catch(IOException e){
+            errprintln(
+                    "標準出力に書き込めません。");
+            exit(1);
+            return null;
+        }
+        return writer;
+    }
+
+    /**
+     * ローカルファイルへの出力先を得る。
+     * @param outdir 出力ディレクトリ
+     * @param landDef 国情報
+     * @param vid 村番号
+     * @return 出力先
+     */
+    public static Writer getFileWriter(String outdir,
+                                         LandDef landDef,
+                                         int vid ){
+            File outFile = new File(outdir);
+            if( ! outFile.exists() ){
+                errprintln(
+                        outdir + " が存在しません。");
+                exit(1);
+                return null;
+            }
+            if( ! outFile.isDirectory() ){
+                errprintln(
+                        outdir + " はディレクトリではありません。");
+                exit(1);
+                return null;
+            }
+            if( ! outFile.canWrite() ){
+                errprintln(
+                        outdir + " に書き込めません。");
+                exit(1);
+                return null;
+            }
+            String fname = MessageFormat.format(
+                "jin_{0}_{1,number,#00000}.xml", landDef.getLandId(), vid);
+            File xmlFile = new File(outFile, fname);
+            boolean created;
+            try{
+                created = xmlFile.createNewFile();
+            }catch(IOException e){
+                errprintln(
+                        xmlFile.getName() + " が作成できません。");
+                exit(1);
+                return null;
+            }
+            if( ! created ){
+                errprintln(
+                        fname + " が既に" + outdir + "に存在します。");
+                exit(1);
+                return null;
+            }
+            /* JRE 1.6 only
+            xmlFile.setReadable(true);
+            xmlFile.setWritable(true);
+            xmlFile.setExecutable(false, false);
+            */
+            Writer writer;
+            try{
+                OutputStream ostream;
+                ostream = new FileOutputStream(xmlFile);
+                ostream = new BufferedOutputStream(ostream, 4 * 1024);
+                writer = new OutputStreamWriter(ostream, "UTF-8");
+                writer = new BufferedWriter(writer, 4 * 1024);
+            }catch(IOException e){
+                errprintln(
+                        xmlFile.getName() + " に書き込めません。");
+                exit(1);
+                return null;
+            }
+
+            return writer;
+    }
+
+    /**
+     * スタートアップエントリ。
+     * @param args 引数
+     */
+    public static void main(String[] args){
+        parseOption(args);
+        exit(0);
+        return;
+    }
+
+}
index 74c8575..2ec3228 100644 (file)
-/*\r
- * Multiplex Writer\r
- *\r
- * License : The MIT License\r
- * Copyright(c) 2008 olyutorskii\r
- */\r
-\r
-package jp.sourceforge.jindolf.archiver;\r
-\r
-import java.io.IOException;\r
-import java.io.Writer;\r
-import java.util.Collections;\r
-import java.util.LinkedList;\r
-import java.util.List;\r
-\r
-/**\r
- * Writerのマルチプレクサ。\r
- */\r
-public class MultiPlexer extends Writer{\r
-\r
-    private final List<Writer> childs = new LinkedList<Writer>();\r
-\r
-    /**\r
-     * コンストラクタ。\r
-     */\r
-    public MultiPlexer(){\r
-        this(null);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * コンストラクタ。\r
-     * @param writer 初期Writer\r
-     */\r
-    public MultiPlexer(Writer writer){\r
-        super();\r
-\r
-        if(writer != null){\r
-            this.childs.add(writer);\r
-        }\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * Writerを追加する。\r
-     * @param writer 追加するWriter。nullなら無視。\r
-     */\r
-    public void addWriter(Writer writer){\r
-        if(writer == null) return;\r
-        this.childs.add(writer);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 出力するWriterの一覧を得る。\r
-     * @return Writer一覧。\r
-     */\r
-    public List<Writer> getWriterList(){\r
-        return Collections.unmodifiableList(this.childs);\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @throws IOException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void close() throws IOException{\r
-        for(Writer writer : this.childs){\r
-            writer.close();\r
-        }\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @throws IOException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void flush() throws IOException{\r
-        for(Writer writer : this.childs){\r
-            writer.flush();\r
-        }\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param cbuf {@inheritDoc}\r
-     * @param off {@inheritDoc}\r
-     * @param len {@inheritDoc}\r
-     * @throws IOException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void write(char[] cbuf, int off, int len) throws IOException{\r
-        for(Writer writer : this.childs){\r
-            writer.write(cbuf, off, len);\r
-        }\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param csq {@inheritDoc}\r
-     * @return {@inheritDoc}\r
-     * @throws IOException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public Writer append(CharSequence csq) throws IOException{\r
-        for(Writer writer : this.childs){\r
-            writer.append(csq);\r
-        }\r
-        return this;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param csq {@inheritDoc}\r
-     * @param start {@inheritDoc}\r
-     * @param end {@inheritDoc}\r
-     * @return {@inheritDoc}\r
-     * @throws IOException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public Writer append(CharSequence csq, int start, int end)\r
-            throws IOException{\r
-        for(Writer writer : this.childs){\r
-            writer.append(csq, start, end);\r
-        }\r
-        return this;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param c {@inheritDoc}\r
-     * @return {@inheritDoc}\r
-     * @throws IOException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public Writer append(char c) throws IOException{\r
-        for(Writer writer : this.childs){\r
-            writer.append(c);\r
-        }\r
-        return this;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param c {@inheritDoc}\r
-     * @throws IOException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void write(int c) throws IOException{\r
-        for(Writer writer : this.childs){\r
-            writer.write(c);\r
-        }\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param cbuf {@inheritDoc}\r
-     * @throws IOException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void write(char[] cbuf) throws IOException{\r
-        for(Writer writer : this.childs){\r
-            writer.write(cbuf);\r
-        }\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param str {@inheritDoc}\r
-     * @throws IOException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void write(String str) throws IOException{\r
-        for(Writer writer : this.childs){\r
-            writer.write(str);\r
-        }\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * {@inheritDoc}\r
-     * @param str {@inheritDoc}\r
-     * @param off {@inheritDoc}\r
-     * @param len {@inheritDoc}\r
-     * @throws IOException {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void write(String str, int off, int len) throws IOException{\r
-        for(Writer writer : this.childs){\r
-            writer.write(str, off, len);\r
-        }\r
-        return;\r
-    }\r
-\r
-}\r
+/*
+ * Multiplex Writer
+ *
+ * License : The MIT License
+ * Copyright(c) 2008 olyutorskii
+ */
+
+package jp.sourceforge.jindolf.archiver;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Writerのマルチプレクサ。
+ */
+public class MultiPlexer extends Writer{
+
+    private final List<Writer> childs = new LinkedList<Writer>();
+
+    /**
+     * コンストラクタ。
+     */
+    public MultiPlexer(){
+        this(null);
+        return;
+    }
+
+    /**
+     * コンストラクタ。
+     * @param writer 初期Writer
+     */
+    public MultiPlexer(Writer writer){
+        super();
+
+        if(writer != null){
+            this.childs.add(writer);
+        }
+
+        return;
+    }
+
+    /**
+     * Writerを追加する。
+     * @param writer 追加するWriter。nullなら無視。
+     */
+    public void addWriter(Writer writer){
+        if(writer == null) return;
+        this.childs.add(writer);
+        return;
+    }
+
+    /**
+     * 出力するWriterの一覧を得る。
+     * @return Writer一覧。
+     */
+    public List<Writer> getWriterList(){
+        return Collections.unmodifiableList(this.childs);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws IOException {@inheritDoc}
+     */
+    @Override
+    public void close() throws IOException{
+        for(Writer writer : this.childs){
+            writer.close();
+        }
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws IOException {@inheritDoc}
+     */
+    @Override
+    public void flush() throws IOException{
+        for(Writer writer : this.childs){
+            writer.flush();
+        }
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param cbuf {@inheritDoc}
+     * @param off {@inheritDoc}
+     * @param len {@inheritDoc}
+     * @throws IOException {@inheritDoc}
+     */
+    @Override
+    public void write(char[] cbuf, int off, int len) throws IOException{
+        for(Writer writer : this.childs){
+            writer.write(cbuf, off, len);
+        }
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param csq {@inheritDoc}
+     * @return {@inheritDoc}
+     * @throws IOException {@inheritDoc}
+     */
+    @Override
+    public Writer append(CharSequence csq) throws IOException{
+        for(Writer writer : this.childs){
+            writer.append(csq);
+        }
+        return this;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param csq {@inheritDoc}
+     * @param start {@inheritDoc}
+     * @param end {@inheritDoc}
+     * @return {@inheritDoc}
+     * @throws IOException {@inheritDoc}
+     */
+    @Override
+    public Writer append(CharSequence csq, int start, int end)
+            throws IOException{
+        for(Writer writer : this.childs){
+            writer.append(csq, start, end);
+        }
+        return this;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param c {@inheritDoc}
+     * @return {@inheritDoc}
+     * @throws IOException {@inheritDoc}
+     */
+    @Override
+    public Writer append(char c) throws IOException{
+        for(Writer writer : this.childs){
+            writer.append(c);
+        }
+        return this;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param c {@inheritDoc}
+     * @throws IOException {@inheritDoc}
+     */
+    @Override
+    public void write(int c) throws IOException{
+        for(Writer writer : this.childs){
+            writer.write(c);
+        }
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param cbuf {@inheritDoc}
+     * @throws IOException {@inheritDoc}
+     */
+    @Override
+    public void write(char[] cbuf) throws IOException{
+        for(Writer writer : this.childs){
+            writer.write(cbuf);
+        }
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param str {@inheritDoc}
+     * @throws IOException {@inheritDoc}
+     */
+    @Override
+    public void write(String str) throws IOException{
+        for(Writer writer : this.childs){
+            writer.write(str);
+        }
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param str {@inheritDoc}
+     * @param off {@inheritDoc}
+     * @param len {@inheritDoc}
+     * @throws IOException {@inheritDoc}
+     */
+    @Override
+    public void write(String str, int off, int len) throws IOException{
+        for(Writer writer : this.childs){
+            writer.write(str, off, len);
+        }
+        return;
+    }
+
+}
index 7f3b4c2..b2b7831 100644 (file)
-/*\r
- * period model\r
- *\r
- * License : The MIT License\r
- * Copyright(c) 2008 olyutorskii\r
- */\r
-\r
-package jp.sourceforge.jindolf.archiver;\r
-\r
-import java.io.IOException;\r
-import java.io.Writer;\r
-import java.net.URI;\r
-import java.util.LinkedList;\r
-import java.util.List;\r
-import jp.sourceforge.jindolf.corelib.DisclosureType;\r
-import jp.sourceforge.jindolf.corelib.SysEventType;\r
-import jp.sourceforge.jindolf.parser.DecodedContent;\r
-\r
-/**\r
- * Periodモデル。\r
- */\r
-public class PeriodData{\r
-\r
-    private final VillageData parent;\r
-    private final PeriodResource resource;\r
-    private DecodedContent loginName = new DecodedContent("");\r
-    private int commitMonth;\r
-    private int commitDay;\r
-    private int commitHour;\r
-    private int commitMinute;\r
-    private DisclosureType disclosureType = DisclosureType.HOT;\r
-    private boolean hasMurderResult = false;\r
-\r
-    private final List<TopicData> topicList = new LinkedList<TopicData>();\r
-\r
-    /**\r
-     * コンストラクタ。\r
-     * @param parent 所属村\r
-     * @param resource ロード元情報\r
-     */\r
-    public PeriodData(VillageData parent, PeriodResource resource){\r
-        super();\r
-        this.parent = parent;\r
-        this.resource = resource;\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * ロード時のログイン名を取得する。\r
-     * @return ログイン名\r
-     */\r
-    public DecodedContent getLoginName(){\r
-        return this.loginName;\r
-    }\r
-\r
-    /**\r
-     * ロード時のログイン名を設定する。\r
-     * @param loginName ログイン名\r
-     */\r
-    public void setLoginName(DecodedContent loginName){\r
-        this.loginName = loginName;\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * コミット月を取得する。\r
-     * @return コミット月\r
-     */\r
-    public int getCommitMonth(){\r
-        return this.commitMonth;\r
-    }\r
-\r
-    /**\r
-     * コミット月を設定する。\r
-     * @param commitMonth コミット月\r
-     */\r
-    public void setCommitMonth(int commitMonth){\r
-        this.commitMonth = commitMonth;\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * コミット日を取得する。\r
-     * @return コミット日\r
-     */\r
-    public int getCommitDay(){\r
-        return this.commitDay;\r
-    }\r
-\r
-    /**\r
-     * コミット日を設定する。\r
-     * @param commitDay コミット日\r
-     */\r
-    public void setCommitDay(int commitDay){\r
-        this.commitDay = commitDay;\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * コミット時を取得する。\r
-     * @return コミット時\r
-     */\r
-    public int getCommitHour(){\r
-        return this.commitHour;\r
-    }\r
-\r
-    /**\r
-     * コミット時を設定する。\r
-     * @param commitHour コミット時\r
-     */\r
-    public void setCommitHour(int commitHour){\r
-        this.commitHour = commitHour;\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * コミット分を取得する。\r
-     * @return コミット分\r
-     */\r
-    public int getCommitMinute(){\r
-        return this.commitMinute;\r
-    }\r
-\r
-    /**\r
-     * コミット分を設定する。\r
-     * @param commitMinute コミット分\r
-     */\r
-    public void setCommitMinute(int commitMinute){\r
-        this.commitMinute = commitMinute;\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 開示状況を取得する。\r
-     * @return 開示状況\r
-     */\r
-    public DisclosureType getDisclosureType(){\r
-        return this.disclosureType;\r
-    }\r
-\r
-    /**\r
-     * 開示状況を設定する。\r
-     * @param type 開示状況\r
-     */\r
-    public void setDisclosureType(DisclosureType type){\r
-        this.disclosureType = type;\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 襲撃結果イベントが既に格納されているか確認する。\r
-     * @return 襲撃結果があればtrue\r
-     */\r
-    public boolean hasMurderResult(){\r
-        return this.hasMurderResult;\r
-    }\r
-\r
-    /**\r
-     * TopicDataを追加する。\r
-     * 襲撃結果の有無も判定される。\r
-     * @param topicData TopiData\r
-     */\r
-    public void addTopicData(TopicData topicData){\r
-        this.topicList.add(topicData);\r
-\r
-        if(topicData instanceof EventData){\r
-            EventData event = (EventData) topicData;\r
-            SysEventType type = event.getEventType();\r
-            if(   type == SysEventType.MURDERED\r
-               || type == SysEventType.NOMURDER){\r
-                this.hasMurderResult = true;\r
-            }\r
-        }\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * period要素をXML出力する。\r
-     * @param writer 出力先\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public void dumpXml(Writer writer) throws IOException{\r
-        writer.append("<period\n");\r
-\r
-        String ptype;\r
-        switch(this.resource.getPeriodType()){\r
-        case PROLOGUE:\r
-            ptype = "prologue";\r
-            break;\r
-        case PROGRESS:\r
-            ptype = "progress";\r
-            break;\r
-        case EPILOGUE:\r
-            ptype = "epilogue";\r
-            break;\r
-        default:\r
-            throw new IllegalArgumentException();\r
-        }\r
-\r
-        XmlUtils.indent(writer, 1);\r
-        XmlUtils.attrOut(writer, "type", ptype);\r
-\r
-        writer.append(' ');\r
-        XmlUtils.attrOut(writer,\r
-                "day", Integer.toString(this.resource.getDay()));\r
-        writer.append('\n');\r
-\r
-        if(this.disclosureType != DisclosureType.COMPLETE){\r
-            XmlUtils.indent(writer, 1);\r
-            XmlUtils.attrOut(writer,\r
-                    "disclosure", this.disclosureType.getXmlName());\r
-            writer.append('\n');\r
-        }\r
-\r
-        XmlUtils.indent(writer, 1);\r
-        XmlUtils.dateAttrOut(writer, "nextCommitDay",\r
-                             this.commitMonth, this.commitDay);\r
-\r
-        writer.append(' ');\r
-        XmlUtils.timeAttrOut(writer,\r
-                             "commitTime",\r
-                             this.commitHour, this.commitMinute);\r
-        writer.append('\n');\r
-\r
-        URI baseUri   = URI.create(this.parent.getBaseUri());\r
-        URI periodUri = URI.create(this.resource.getOrigUrlText());\r
-        URI relativeUri = baseUri.relativize(periodUri);\r
-        XmlUtils.indent(writer, 1);\r
-        XmlUtils.attrOut(writer, "sourceURI", relativeUri.toString());\r
-        writer.append('\n');\r
-\r
-        long downTimeMs = this.resource.getDownTimeMs();\r
-        XmlUtils.indent(writer, 1);\r
-        XmlUtils.dateTimeAttr(writer, "loadedTime", downTimeMs);\r
-        writer.append('\n');\r
-\r
-        if(this.loginName.length() > 0){\r
-            XmlUtils.indent(writer, 1);\r
-            XmlUtils.attrOut(writer, "loadedBy", this.loginName.toString());\r
-            writer.append('\n');\r
-        }\r
-\r
-        writer.append(">\n\n");\r
-\r
-        for(TopicData topic : this.topicList){\r
-            topic.dumpXml(writer);\r
-            writer.append('\n');\r
-            writer.flush();\r
-        }\r
-\r
-        writer.append("</period>\n");\r
-\r
-        return;\r
-    }\r
-\r
-}\r
+/*
+ * period model
+ *
+ * License : The MIT License
+ * Copyright(c) 2008 olyutorskii
+ */
+
+package jp.sourceforge.jindolf.archiver;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.net.URI;
+import java.util.LinkedList;
+import java.util.List;
+import jp.sourceforge.jindolf.corelib.DisclosureType;
+import jp.sourceforge.jindolf.corelib.SysEventType;
+import jp.sourceforge.jindolf.parser.DecodedContent;
+
+/**
+ * Periodモデル。
+ */
+public class PeriodData{
+
+    private final VillageData parent;
+    private final PeriodResource resource;
+    private DecodedContent loginName = new DecodedContent("");
+    private int commitMonth;
+    private int commitDay;
+    private int commitHour;
+    private int commitMinute;
+    private DisclosureType disclosureType = DisclosureType.HOT;
+    private boolean hasMurderResult = false;
+
+    private final List<TopicData> topicList = new LinkedList<TopicData>();
+
+    /**
+     * コンストラクタ。
+     * @param parent 所属村
+     * @param resource ロード元情報
+     */
+    public PeriodData(VillageData parent, PeriodResource resource){
+        super();
+        this.parent = parent;
+        this.resource = resource;
+        return;
+    }
+
+    /**
+     * ロード時のログイン名を取得する。
+     * @return ログイン名
+     */
+    public DecodedContent getLoginName(){
+        return this.loginName;
+    }
+
+    /**
+     * ロード時のログイン名を設定する。
+     * @param loginName ログイン名
+     */
+    public void setLoginName(DecodedContent loginName){
+        this.loginName = loginName;
+        return;
+    }
+
+    /**
+     * コミット月を取得する。
+     * @return コミット月
+     */
+    public int getCommitMonth(){
+        return this.commitMonth;
+    }
+
+    /**
+     * コミット月を設定する。
+     * @param commitMonth コミット月
+     */
+    public void setCommitMonth(int commitMonth){
+        this.commitMonth = commitMonth;
+        return;
+    }
+
+    /**
+     * コミット日を取得する。
+     * @return コミット日
+     */
+    public int getCommitDay(){
+        return this.commitDay;
+    }
+
+    /**
+     * コミット日を設定する。
+     * @param commitDay コミット日
+     */
+    public void setCommitDay(int commitDay){
+        this.commitDay = commitDay;
+        return;
+    }
+
+    /**
+     * コミット時を取得する。
+     * @return コミット時
+     */
+    public int getCommitHour(){
+        return this.commitHour;
+    }
+
+    /**
+     * コミット時を設定する。
+     * @param commitHour コミット時
+     */
+    public void setCommitHour(int commitHour){
+        this.commitHour = commitHour;
+        return;
+    }
+
+    /**
+     * コミット分を取得する。
+     * @return コミット分
+     */
+    public int getCommitMinute(){
+        return this.commitMinute;
+    }
+
+    /**
+     * コミット分を設定する。
+     * @param commitMinute コミット分
+     */
+    public void setCommitMinute(int commitMinute){
+        this.commitMinute = commitMinute;
+        return;
+    }
+
+    /**
+     * 開示状況を取得する。
+     * @return 開示状況
+     */
+    public DisclosureType getDisclosureType(){
+        return this.disclosureType;
+    }
+
+    /**
+     * 開示状況を設定する。
+     * @param type 開示状況
+     */
+    public void setDisclosureType(DisclosureType type){
+        this.disclosureType = type;
+        return;
+    }
+
+    /**
+     * 襲撃結果イベントが既に格納されているか確認する。
+     * @return 襲撃結果があればtrue
+     */
+    public boolean hasMurderResult(){
+        return this.hasMurderResult;
+    }
+
+    /**
+     * TopicDataを追加する。
+     * 襲撃結果の有無も判定される。
+     * @param topicData TopiData
+     */
+    public void addTopicData(TopicData topicData){
+        this.topicList.add(topicData);
+
+        if(topicData instanceof EventData){
+            EventData event = (EventData) topicData;
+            SysEventType type = event.getEventType();
+            if(   type == SysEventType.MURDERED
+               || type == SysEventType.NOMURDER){
+                this.hasMurderResult = true;
+            }
+        }
+
+        return;
+    }
+
+    /**
+     * period要素をXML出力する。
+     * @param writer 出力先
+     * @throws IOException 出力エラー
+     */
+    public void dumpXml(Writer writer) throws IOException{
+        writer.append("<period\n");
+
+        String ptype;
+        switch(this.resource.getPeriodType()){
+        case PROLOGUE:
+            ptype = "prologue";
+            break;
+        case PROGRESS:
+            ptype = "progress";
+            break;
+        case EPILOGUE:
+            ptype = "epilogue";
+            break;
+        default:
+            throw new IllegalArgumentException();
+        }
+
+        XmlUtils.indent(writer, 1);
+        XmlUtils.attrOut(writer, "type", ptype);
+
+        writer.append(' ');
+        XmlUtils.attrOut(writer,
+                "day", Integer.toString(this.resource.getDay()));
+        writer.append('\n');
+
+        if(this.disclosureType != DisclosureType.COMPLETE){
+            XmlUtils.indent(writer, 1);
+            XmlUtils.attrOut(writer,
+                    "disclosure", this.disclosureType.getXmlName());
+            writer.append('\n');
+        }
+
+        XmlUtils.indent(writer, 1);
+        XmlUtils.dateAttrOut(writer, "nextCommitDay",
+                             this.commitMonth, this.commitDay);
+
+        writer.append(' ');
+        XmlUtils.timeAttrOut(writer,
+                             "commitTime",
+                             this.commitHour, this.commitMinute);
+        writer.append('\n');
+
+        URI baseUri   = URI.create(this.parent.getBaseUri());
+        URI periodUri = URI.create(this.resource.getOrigUrlText());
+        URI relativeUri = baseUri.relativize(periodUri);
+        XmlUtils.indent(writer, 1);
+        XmlUtils.attrOut(writer, "sourceURI", relativeUri.toString());
+        writer.append('\n');
+
+        long downTimeMs = this.resource.getDownTimeMs();
+        XmlUtils.indent(writer, 1);
+        XmlUtils.dateTimeAttr(writer, "loadedTime", downTimeMs);
+        writer.append('\n');
+
+        if(this.loginName.length() > 0){
+            XmlUtils.indent(writer, 1);
+            XmlUtils.attrOut(writer, "loadedBy", this.loginName.toString());
+            writer.append('\n');
+        }
+
+        writer.append(">\n\n");
+
+        for(TopicData topic : this.topicList){
+            topic.dumpXml(writer);
+            writer.append('\n');
+            writer.flush();
+        }
+
+        writer.append("</period>\n");
+
+        return;
+    }
+
+}
index efcdc65..b6290cc 100644 (file)
-/*\r
- * Period resource\r
- *\r
- * License : The MIT License\r
- * Copyright(c) 2008 olyutorskii\r
- */\r
-\r
-package jp.sourceforge.jindolf.archiver;\r
-\r
-import java.net.URL;\r
-import jp.sourceforge.jindolf.corelib.LandDef;\r
-import jp.sourceforge.jindolf.corelib.PeriodType;\r
-\r
-/**\r
- * Periodのロード元情報。\r
- */\r
-public class PeriodResource{\r
-\r
-    private final LandDef landDef;\r
-    private final int villageId;\r
-    private final PeriodType periodType;\r
-    private final int day;\r
-    private final String origUrlText;\r
-    private long downTimeMs;\r
-    private URL resourceUrl;\r
-\r
-    /**\r
-     * コンストラクタ。\r
-     * @param landDef 国情報\r
-     * @param villageId 村ID\r
-     * @param periodType Period種別\r
-     * @param day 日付\r
-     * @param origUrlText ロード元URI文字列\r
-     * @param downTimeMs ロード時刻\r
-     * @param resourceUrl ロード元URL\r
-     */\r
-    public PeriodResource(LandDef landDef,\r
-                            int villageId,\r
-                            PeriodType periodType,\r
-                            int day,\r
-                            String origUrlText,\r
-                            long downTimeMs,\r
-                            URL resourceUrl ) {\r
-        super();\r
-\r
-        this.landDef = landDef;\r
-        this.villageId = villageId;\r
-        this.periodType = periodType;\r
-        this.day = day;\r
-        this.origUrlText = origUrlText;\r
-        this.downTimeMs = downTimeMs;\r
-        this.resourceUrl = resourceUrl;\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 国情報を取得する。\r
-     * @return 国情報\r
-     */\r
-    public LandDef getLandDef(){\r
-        return landDef;\r
-    }\r
-\r
-    /**\r
-     * 村番号を取得する。\r
-     * @return 村番号\r
-     */\r
-    public int getVillageId(){\r
-        return villageId;\r
-    }\r
-\r
-    /**\r
-     * Periodの種別を取得する。\r
-     * @return Period種別\r
-     */\r
-    public PeriodType getPeriodType(){\r
-        return periodType;\r
-    }\r
-\r
-    /**\r
-     * 日付を取得する。\r
-     * @return 日付\r
-     */\r
-    public int getDay(){\r
-        return day;\r
-    }\r
-\r
-    /**\r
-     * オリジナルのダウンロード元URL文字列を取得する。\r
-     * @return ダウンロード元URL文字列\r
-     */\r
-    public String getOrigUrlText(){\r
-        return origUrlText;\r
-    }\r
-\r
-    /**\r
-     * オリジナルのダウンロード時刻を取得する。\r
-     * @return ダウンロード時刻。エポック秒(ms)\r
-     */\r
-    public long getDownTimeMs(){\r
-        return this.downTimeMs;\r
-    }\r
-\r
-    /**\r
-     * オリジナルのダウンロード時刻を設定する。\r
-     * @param downTimeMs ダウンロード時刻。エポック秒(ms)\r
-     */\r
-    public void setDownTimeMs(long downTimeMs){\r
-        this.downTimeMs = downTimeMs;\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * XHTML格納先URLを取得する。\r
-     * @return 格納先URL\r
-     */\r
-    public URL getResourceUrl(){\r
-        return resourceUrl;\r
-    }\r
-\r
-    /**\r
-     * XHTML格納先URLを設定する。\r
-     * @param resourceUrl 格納先URL\r
-     */\r
-    public void setResourceUrl(URL resourceUrl){\r
-        this.resourceUrl = resourceUrl;\r
-        return;\r
-    }\r
-\r
-}\r
+/*
+ * Period resource
+ *
+ * License : The MIT License
+ * Copyright(c) 2008 olyutorskii
+ */
+
+package jp.sourceforge.jindolf.archiver;
+
+import java.net.URL;
+import jp.sourceforge.jindolf.corelib.LandDef;
+import jp.sourceforge.jindolf.corelib.PeriodType;
+
+/**
+ * Periodのロード元情報。
+ */
+public class PeriodResource{
+
+    private final LandDef landDef;
+    private final int villageId;
+    private final PeriodType periodType;
+    private final int day;
+    private final String origUrlText;
+    private long downTimeMs;
+    private URL resourceUrl;
+
+    /**
+     * コンストラクタ。
+     * @param landDef 国情報
+     * @param villageId 村ID
+     * @param periodType Period種別
+     * @param day 日付
+     * @param origUrlText ロード元URI文字列
+     * @param downTimeMs ロード時刻
+     * @param resourceUrl ロード元URL
+     */
+    public PeriodResource(LandDef landDef,
+                            int villageId,
+                            PeriodType periodType,
+                            int day,
+                            String origUrlText,
+                            long downTimeMs,
+                            URL resourceUrl ) {
+        super();
+
+        this.landDef = landDef;
+        this.villageId = villageId;
+        this.periodType = periodType;
+        this.day = day;
+        this.origUrlText = origUrlText;
+        this.downTimeMs = downTimeMs;
+        this.resourceUrl = resourceUrl;
+
+        return;
+    }
+
+    /**
+     * 国情報を取得する。
+     * @return 国情報
+     */
+    public LandDef getLandDef(){
+        return landDef;
+    }
+
+    /**
+     * 村番号を取得する。
+     * @return 村番号
+     */
+    public int getVillageId(){
+        return villageId;
+    }
+
+    /**
+     * Periodの種別を取得する。
+     * @return Period種別
+     */
+    public PeriodType getPeriodType(){
+        return periodType;
+    }
+
+    /**
+     * 日付を取得する。
+     * @return 日付
+     */
+    public int getDay(){
+        return day;
+    }
+
+    /**
+     * オリジナルのダウンロード元URL文字列を取得する。
+     * @return ダウンロード元URL文字列
+     */
+    public String getOrigUrlText(){
+        return origUrlText;
+    }
+
+    /**
+     * オリジナルのダウンロード時刻を取得する。
+     * @return ダウンロード時刻。エポック秒(ms)
+     */
+    public long getDownTimeMs(){
+        return this.downTimeMs;
+    }
+
+    /**
+     * オリジナルのダウンロード時刻を設定する。
+     * @param downTimeMs ダウンロード時刻。エポック秒(ms)
+     */
+    public void setDownTimeMs(long downTimeMs){
+        this.downTimeMs = downTimeMs;
+        return;
+    }
+
+    /**
+     * XHTML格納先URLを取得する。
+     * @return 格納先URL
+     */
+    public URL getResourceUrl(){
+        return resourceUrl;
+    }
+
+    /**
+     * XHTML格納先URLを設定する。
+     * @param resourceUrl 格納先URL
+     */
+    public void setResourceUrl(URL resourceUrl){
+        this.resourceUrl = resourceUrl;
+        return;
+    }
+
+}
index e8f06ad..93a84cd 100644 (file)
-/*\r
- * talk dialog\r
- *\r
- * License : The MIT License\r
- * Copyright(c) 2008 olyutorskii\r
- */\r
-\r
-package jp.sourceforge.jindolf.archiver;\r
-\r
-import java.io.IOException;\r
-import java.io.Writer;\r
-import jp.sourceforge.jindolf.corelib.TalkType;\r
-\r
-/**\r
- * 発言モデル。\r
- */\r
-public class TalkData extends TopicData{\r
-\r
-    private TalkType talkType = null;\r
-    private AvatarData avatarData = null;\r
-    private String xName;\r
-    private String faceIconUri = null;\r
-    private int hour;\r
-    private int minute;\r
-\r
-    /**\r
-     * コンストラクタ。\r
-     */\r
-    public TalkData(){\r
-        super();\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 発言種別を取得する。\r
-     * @return 発言種別\r
-     */\r
-    public TalkType getTalkType(){\r
-        return this.talkType;\r
-    }\r
-\r
-    /**\r
-     * 発言種別を設定する。\r
-     * @param talkType 発言種別\r
-     */\r
-    public void setTalkType(TalkType talkType){\r
-        this.talkType = talkType;\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 発言したAvatarを取得する。\r
-     * @return 発言Avatar\r
-     */\r
-    public AvatarData getAvatarData(){\r
-        return this.avatarData;\r
-    }\r
-\r
-    /**\r
-     * 発言したAvatarを設定する。\r
-     * @param avatarData 発言Avatar\r
-     */\r
-    public void setAvatarData(AvatarData avatarData){\r
-        this.avatarData = avatarData;\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 元発言のname属性値を取得する。\r
-     * @return name属性値\r
-     */\r
-    public String getXName(){\r
-        return this.xName;\r
-    }\r
-\r
-    /**\r
-     * 元発言のname属性値を設定する。\r
-     * @param xName name属性値\r
-     */\r
-    public void setXName(String xName){\r
-        this.xName = xName;\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 顔アイコン画像URI文字列を取得する。\r
-     * @return 顔アイコン画像URI文字列\r
-     */\r
-    public String getFaceIconUri(){\r
-        return this.faceIconUri;\r
-    }\r
-\r
-    /**\r
-     * 顔アイコン画像URI文字列を設定する。\r
-     * @param faceIconUri 顔アイコン画像URI文字列\r
-     */\r
-    public void setFaceIconUri(String faceIconUri){\r
-        this.faceIconUri = faceIconUri;\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 発言時を取得する。\r
-     * @return 発言時\r
-     */\r
-    public int getHour(){\r
-        return this.hour;\r
-    }\r
-\r
-    /**\r
-     * 発言時を設定する。\r
-     * @param hour 発言時\r
-     */\r
-    public void setHour(int hour){\r
-        this.hour = hour;\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 発言分を取得する。\r
-     * @return 発言分\r
-     */\r
-    public int getMinute(){\r
-        return this.minute;\r
-    }\r
-\r
-    /**\r
-     * 発言分を設定する。\r
-     * @param minute 発言分\r
-     */\r
-    public void setMinute(int minute){\r
-        this.minute = minute;\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * talk要素をXML出力する。\r
-     * @param writer 出力先\r
-     * @throws IOException 出力エラー\r
-     */\r
-    @Override\r
-    public void dumpXml(Writer writer) throws IOException{\r
-        writer.append("<talk\n");\r
-\r
-        String typeStr;\r
-        switch(this.talkType){\r
-        case PUBLIC:\r
-            typeStr = "public";\r
-            break;\r
-        case WOLFONLY:\r
-            typeStr = "wolf";\r
-            break;\r
-        case PRIVATE:\r
-            typeStr = "private";\r
-            break;\r
-        case GRAVE:\r
-            typeStr = "grave";\r
-            break;\r
-        default:\r
-            throw new IllegalArgumentException();\r
-        }\r
-\r
-        XmlUtils.indent(writer, 1);\r
-        XmlUtils.attrOut(writer, "type", typeStr);\r
-\r
-        writer.append(' ');\r
-        XmlUtils.attrOut(writer, "avatarId", this.avatarData.getAvatarId());\r
-        writer.append('\n');\r
-\r
-        XmlUtils.indent(writer, 1);\r
-        XmlUtils.attrOut(writer, "xname", this.xName);\r
-\r
-        writer.append(' ');\r
-        XmlUtils.timeAttrOut(writer, "time", this.hour, this.minute);\r
-        writer.append('\n');\r
-\r
-        if(   this.talkType != TalkType.GRAVE\r
-           && ! this.faceIconUri.equals(this.avatarData.getFaceIconUri()) ){\r
-            XmlUtils.indent(writer, 1);\r
-            XmlUtils.attrOut(writer, "faceIconURI", this.faceIconUri);\r
-            writer.append('\n');\r
-        }\r
-\r
-        writer.append(">\n");\r
-\r
-        dumpLines(writer);\r
-\r
-        writer.append("</talk>\n");\r
-        return;\r
-    }\r
-\r
-}\r
+/*
+ * talk dialog
+ *
+ * License : The MIT License
+ * Copyright(c) 2008 olyutorskii
+ */
+
+package jp.sourceforge.jindolf.archiver;
+
+import java.io.IOException;
+import java.io.Writer;
+import jp.sourceforge.jindolf.corelib.TalkType;
+
+/**
+ * 発言モデル。
+ */
+public class TalkData extends TopicData{
+
+    private TalkType talkType = null;
+    private AvatarData avatarData = null;
+    private String xName;
+    private String faceIconUri = null;
+    private int hour;
+    private int minute;
+
+    /**
+     * コンストラクタ。
+     */
+    public TalkData(){
+        super();
+        return;
+    }
+
+    /**
+     * 発言種別を取得する。
+     * @return 発言種別
+     */
+    public TalkType getTalkType(){
+        return this.talkType;
+    }
+
+    /**
+     * 発言種別を設定する。
+     * @param talkType 発言種別
+     */
+    public void setTalkType(TalkType talkType){
+        this.talkType = talkType;
+        return;
+    }
+
+    /**
+     * 発言したAvatarを取得する。
+     * @return 発言Avatar
+     */
+    public AvatarData getAvatarData(){
+        return this.avatarData;
+    }
+
+    /**
+     * 発言したAvatarを設定する。
+     * @param avatarData 発言Avatar
+     */
+    public void setAvatarData(AvatarData avatarData){
+        this.avatarData = avatarData;
+        return;
+    }
+
+    /**
+     * 元発言のname属性値を取得する。
+     * @return name属性値
+     */
+    public String getXName(){
+        return this.xName;
+    }
+
+    /**
+     * 元発言のname属性値を設定する。
+     * @param xName name属性値
+     */
+    public void setXName(String xName){
+        this.xName = xName;
+        return;
+    }
+
+    /**
+     * 顔アイコン画像URI文字列を取得する。
+     * @return 顔アイコン画像URI文字列
+     */
+    public String getFaceIconUri(){
+        return this.faceIconUri;
+    }
+
+    /**
+     * 顔アイコン画像URI文字列を設定する。
+     * @param faceIconUri 顔アイコン画像URI文字列
+     */
+    public void setFaceIconUri(String faceIconUri){
+        this.faceIconUri = faceIconUri;
+        return;
+    }
+
+    /**
+     * 発言時を取得する。
+     * @return 発言時
+     */
+    public int getHour(){
+        return this.hour;
+    }
+
+    /**
+     * 発言時を設定する。
+     * @param hour 発言時
+     */
+    public void setHour(int hour){
+        this.hour = hour;
+        return;
+    }
+
+    /**
+     * 発言分を取得する。
+     * @return 発言分
+     */
+    public int getMinute(){
+        return this.minute;
+    }
+
+    /**
+     * 発言分を設定する。
+     * @param minute 発言分
+     */
+    public void setMinute(int minute){
+        this.minute = minute;
+        return;
+    }
+
+    /**
+     * talk要素をXML出力する。
+     * @param writer 出力先
+     * @throws IOException 出力エラー
+     */
+    @Override
+    public void dumpXml(Writer writer) throws IOException{
+        writer.append("<talk\n");
+
+        String typeStr;
+        switch(this.talkType){
+        case PUBLIC:
+            typeStr = "public";
+            break;
+        case WOLFONLY:
+            typeStr = "wolf";
+            break;
+        case PRIVATE:
+            typeStr = "private";
+            break;
+        case GRAVE:
+            typeStr = "grave";
+            break;
+        default:
+            throw new IllegalArgumentException();
+        }
+
+        XmlUtils.indent(writer, 1);
+        XmlUtils.attrOut(writer, "type", typeStr);
+
+        writer.append(' ');
+        XmlUtils.attrOut(writer, "avatarId", this.avatarData.getAvatarId());
+        writer.append('\n');
+
+        XmlUtils.indent(writer, 1);
+        XmlUtils.attrOut(writer, "xname", this.xName);
+
+        writer.append(' ');
+        XmlUtils.timeAttrOut(writer, "time", this.hour, this.minute);
+        writer.append('\n');
+
+        if(   this.talkType != TalkType.GRAVE
+           && ! this.faceIconUri.equals(this.avatarData.getFaceIconUri()) ){
+            XmlUtils.indent(writer, 1);
+            XmlUtils.attrOut(writer, "faceIconURI", this.faceIconUri);
+            writer.append('\n');
+        }
+
+        writer.append(">\n");
+
+        dumpLines(writer);
+
+        writer.append("</talk>\n");
+        return;
+    }
+
+}
index c46af43..6df83a0 100644 (file)
-/*\r
- * topic data\r
- *\r
- * License : The MIT License\r
- * Copyright(c) 2008 olyutorskii\r
- */\r
-\r
-package jp.sourceforge.jindolf.archiver;\r
-\r
-import java.io.IOException;\r
-import java.io.Writer;\r
-import java.util.LinkedList;\r
-import java.util.List;\r
-import jp.sourceforge.jindolf.parser.DecodedContent;\r
-\r
-/**\r
- * テキスト行の集合。\r
- */\r
-public abstract class TopicData{\r
-\r
-    private static final DecodedContent BREAK = new DecodedContent("\n");\r
-\r
-    private final List<DecodedContent> lineList =\r
-            new LinkedList<DecodedContent>();\r
-\r
-    /**\r
-     * コンストラクタ。\r
-     */\r
-    protected TopicData(){\r
-        super();\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 行を追加する。\r
-     * @param content 行を構成する文字列\r
-     */\r
-    public void addLine(DecodedContent content){\r
-        this.lineList.add(content);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 行ブレークを追加する。\r
-     */\r
-    public void addBreak(){\r
-        this.lineList.add(BREAK);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 行数を取得する。\r
-     * @return 行数\r
-     */\r
-    public int getLineNum(){\r
-        return this.lineList.size();\r
-    }\r
-\r
-    /**\r
-     * 最初の行を取得する。\r
-     * @return 最初の行\r
-     */\r
-    public DecodedContent get1stLine(){\r
-        return this.lineList.get(0);\r
-    }\r
-\r
-    /**\r
-     * 1行li要素をXML出力する。\r
-     * @param writer 出力先\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public void dumpLines(Writer writer) throws IOException{\r
-        DecodedContent lastLine = null;\r
-        DecodedContent lastContent = null;\r
-\r
-        for(DecodedContent content : this.lineList){\r
-            lastContent = content;\r
-            if(content == BREAK){\r
-                if(lastLine != null){\r
-                    writer.append("</li>\n");\r
-                    lastLine = null;\r
-                }else{\r
-                    writer.append("<li/>\n");\r
-                }\r
-            }else{\r
-                if(lastLine == null){\r
-                    writer.append("<li>");\r
-                }\r
-                XmlUtils.dumpDecodedContent(writer, content);\r
-                lastLine = content;\r
-            }\r
-        }\r
-\r
-        if(lastLine != null){\r
-            writer.append("</li>\n");\r
-        }else if(lastContent == BREAK){\r
-            writer.append("<li/>\n");\r
-        }\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 要素をXML出力する。\r
-     * @param writer 出力先\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public abstract void dumpXml(Writer writer) throws IOException;\r
-\r
-}\r
+/*
+ * topic data
+ *
+ * License : The MIT License
+ * Copyright(c) 2008 olyutorskii
+ */
+
+package jp.sourceforge.jindolf.archiver;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.LinkedList;
+import java.util.List;
+import jp.sourceforge.jindolf.parser.DecodedContent;
+
+/**
+ * テキスト行の集合。
+ */
+public abstract class TopicData{
+
+    private static final DecodedContent BREAK = new DecodedContent("\n");
+
+    private final List<DecodedContent> lineList =
+            new LinkedList<DecodedContent>();
+
+    /**
+     * コンストラクタ。
+     */
+    protected TopicData(){
+        super();
+        return;
+    }
+
+    /**
+     * 行を追加する。
+     * @param content 行を構成する文字列
+     */
+    public void addLine(DecodedContent content){
+        this.lineList.add(content);
+        return;
+    }
+
+    /**
+     * 行ブレークを追加する。
+     */
+    public void addBreak(){
+        this.lineList.add(BREAK);
+        return;
+    }
+
+    /**
+     * 行数を取得する。
+     * @return 行数
+     */
+    public int getLineNum(){
+        return this.lineList.size();
+    }
+
+    /**
+     * 最初の行を取得する。
+     * @return 最初の行
+     */
+    public DecodedContent get1stLine(){
+        return this.lineList.get(0);
+    }
+
+    /**
+     * 1行li要素をXML出力する。
+     * @param writer 出力先
+     * @throws IOException 出力エラー
+     */
+    public void dumpLines(Writer writer) throws IOException{
+        DecodedContent lastLine = null;
+        DecodedContent lastContent = null;
+
+        for(DecodedContent content : this.lineList){
+            lastContent = content;
+            if(content == BREAK){
+                if(lastLine != null){
+                    writer.append("</li>\n");
+                    lastLine = null;
+                }else{
+                    writer.append("<li/>\n");
+                }
+            }else{
+                if(lastLine == null){
+                    writer.append("<li>");
+                }
+                XmlUtils.dumpDecodedContent(writer, content);
+                lastLine = content;
+            }
+        }
+
+        if(lastLine != null){
+            writer.append("</li>\n");
+        }else if(lastContent == BREAK){
+            writer.append("<li/>\n");
+        }
+
+        return;
+    }
+
+    /**
+     * 要素をXML出力する。
+     * @param writer 出力先
+     * @throws IOException 出力エラー
+     */
+    public abstract void dumpXml(Writer writer) throws IOException;
+
+}
index b747bc0..c08fe39 100644 (file)
-/*\r
- * XML-validation task\r
- *\r
- * License : The MIT License\r
- * Copyright(c) 2008 olyutorskii\r
- */\r
-\r
-package jp.sourceforge.jindolf.archiver;\r
-\r
-import java.io.IOException;\r
-import java.io.PipedReader;\r
-import java.io.PipedWriter;\r
-import java.io.Reader;\r
-import java.io.Writer;\r
-import javax.xml.XMLConstants;\r
-import javax.xml.transform.Source;\r
-import javax.xml.transform.stream.StreamSource;\r
-import javax.xml.validation.Schema;\r
-import javax.xml.validation.SchemaFactory;\r
-import javax.xml.validation.Validator;\r
-import org.xml.sax.SAXException;\r
-\r
-/**\r
- * XML検証タスク。\r
- */\r
-public class ValidateTask implements Runnable{\r
-\r
-    private static final SchemaFactory FACTORY =\r
-            SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);\r
-\r
-    private final Validator validator;\r
-    private final Source source;\r
-\r
-    /**\r
-     * コンストラクタ。\r
-     * @param reader 文字入力\r
-     * @throws SAXException 内部エラー\r
-     */\r
-    protected ValidateTask(Reader reader) throws SAXException{\r
-        super();\r
-        Schema schema = FACTORY.newSchema();\r
-        this.validator = schema.newValidator();\r
-        this.source = new StreamSource(reader);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 検証タスク。\r
-     * {@inheritDoc}\r
-     */\r
-    @Override\r
-    public void run(){\r
-        boolean done = false;\r
-        try{\r
-            this.validator.validate(this.source);\r
-            done = true;\r
-        }catch(IOException e){\r
-            e.printStackTrace(System.err);\r
-            System.err.println("XML検証に失敗しました。");\r
-        }catch(SAXException e){\r
-            e.printStackTrace(System.err);\r
-            System.err.println("XML検証に失敗しました。");\r
-        }finally{\r
-            if( ! done ) System.exit(1);\r
-        }\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 文字出力を横取りしバックグラウンドで検証を行うWriterを生成する。\r
-     * @param writer 元出力\r
-     * @return 新しい出力\r
-     */\r
-    public static Writer wrapValidator(Writer writer){\r
-        PipedReader reader = new PipedReader();\r
-        Writer pipeWriter;\r
-        try{\r
-            pipeWriter = new PipedWriter(reader);\r
-        }catch(IOException e){\r
-            e.printStackTrace(System.err);\r
-            System.err.println("処理を続行できません。");\r
-            System.exit(1);\r
-            return null;\r
-        }\r
-\r
-        MultiPlexer mtplx = new MultiPlexer();\r
-        mtplx.addWriter(writer);\r
-        mtplx.addWriter(pipeWriter);\r
-\r
-        Runnable task;\r
-        try{\r
-            task = new ValidateTask(reader);\r
-        }catch(SAXException e){\r
-            e.printStackTrace(System.err);\r
-            System.err.println("処理を続行できません。");\r
-            System.exit(1);\r
-            return null;\r
-        }\r
-        Thread th = new Thread(task);\r
-        th.setDaemon(false);\r
-        th.start();\r
-\r
-        return mtplx;\r
-    }\r
-\r
-}\r
+/*
+ * XML-validation task
+ *
+ * License : The MIT License
+ * Copyright(c) 2008 olyutorskii
+ */
+
+package jp.sourceforge.jindolf.archiver;
+
+import java.io.IOException;
+import java.io.PipedReader;
+import java.io.PipedWriter;
+import java.io.Reader;
+import java.io.Writer;
+import javax.xml.XMLConstants;
+import javax.xml.transform.Source;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+import javax.xml.validation.Validator;
+import org.xml.sax.SAXException;
+
+/**
+ * XML検証タスク。
+ */
+public class ValidateTask implements Runnable{
+
+    private static final SchemaFactory FACTORY =
+            SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+
+    private final Validator validator;
+    private final Source source;
+
+    /**
+     * コンストラクタ。
+     * @param reader 文字入力
+     * @throws SAXException 内部エラー
+     */
+    protected ValidateTask(Reader reader) throws SAXException{
+        super();
+        Schema schema = FACTORY.newSchema();
+        this.validator = schema.newValidator();
+        this.source = new StreamSource(reader);
+        return;
+    }
+
+    /**
+     * 検証タスク。
+     * {@inheritDoc}
+     */
+    @Override
+    public void run(){
+        boolean done = false;
+        try{
+            this.validator.validate(this.source);
+            done = true;
+        }catch(IOException e){
+            e.printStackTrace(System.err);
+            System.err.println("XML検証に失敗しました。");
+        }catch(SAXException e){
+            e.printStackTrace(System.err);
+            System.err.println("XML検証に失敗しました。");
+        }finally{
+            if( ! done ) System.exit(1);
+        }
+
+        return;
+    }
+
+    /**
+     * 文字出力を横取りしバックグラウンドで検証を行うWriterを生成する。
+     * @param writer 元出力
+     * @return 新しい出力
+     */
+    public static Writer wrapValidator(Writer writer){
+        PipedReader reader = new PipedReader();
+        Writer pipeWriter;
+        try{
+            pipeWriter = new PipedWriter(reader);
+        }catch(IOException e){
+            e.printStackTrace(System.err);
+            System.err.println("処理を続行できません。");
+            System.exit(1);
+            return null;
+        }
+
+        MultiPlexer mtplx = new MultiPlexer();
+        mtplx.addWriter(writer);
+        mtplx.addWriter(pipeWriter);
+
+        Runnable task;
+        try{
+            task = new ValidateTask(reader);
+        }catch(SAXException e){
+            e.printStackTrace(System.err);
+            System.err.println("処理を続行できません。");
+            System.exit(1);
+            return null;
+        }
+        Thread th = new Thread(task);
+        th.setDaemon(false);
+        th.start();
+
+        return mtplx;
+    }
+
+}
index 4f94085..d19ccf0 100644 (file)
-/*\r
- * village data\r
- *\r
- * License : The MIT License\r
- * Copyright(c) 2008 olyutorskii\r
- */\r
-\r
-package jp.sourceforge.jindolf.archiver;\r
-\r
-import java.io.IOException;\r
-import java.io.Writer;\r
-import java.util.Collections;\r
-import java.util.LinkedList;\r
-import java.util.List;\r
-import jp.sourceforge.jindolf.corelib.DisclosureType;\r
-import jp.sourceforge.jindolf.corelib.LandDef;\r
-import jp.sourceforge.jindolf.corelib.PeriodType;\r
-import jp.sourceforge.jindolf.corelib.PreDefAvatar;\r
-\r
-/**\r
- * 村のデータモデル。\r
- * villageタグに相当。\r
- */\r
-public class VillageData{\r
-\r
-    private final List<PeriodResource> resourceList;\r
-\r
-    private final LandDef landDef;\r
-    private final int villageId;\r
-    private final String baseUri;\r
-\r
-    private String fullName = "";\r
-    private int commitHour = -1;\r
-    private int commitMinute = -1;\r
-    private String graveIconUri;\r
-\r
-    private final List<AvatarData> avatarList = new LinkedList<AvatarData>();\r
-    private int undefAvatarNo = 1;\r
-\r
-    private final List<PeriodData> periodList = new LinkedList<PeriodData>();\r
-\r
-\r
-    /**\r
-     * コンストラクタ。\r
-     * @param resourceList PeriodResource並び\r
-     */\r
-    public VillageData(List<PeriodResource> resourceList){\r
-        super();\r
-\r
-        validatePeriodResource(resourceList);\r
-\r
-        this.resourceList = new LinkedList<PeriodResource>(resourceList);\r
-\r
-        PeriodResource resource1st = this.resourceList.get(0);\r
-        this.landDef   = resource1st.getLandDef();\r
-        this.villageId = resource1st.getVillageId();\r
-        this.baseUri = getBaseUri(this.resourceList);\r
-\r
-        return;\r
-    }\r
-\r
-\r
-    /**\r
-     * PeriodResourceの組が正当かチェックする。\r
-     * <ul>\r
-     * <li>全て同じ国に属していなければならない\r
-     * <li>全て同じ村に属していなければならない\r
-     * <li>日付は0から始まる連続した数値でなければならない\r
-     * <li>プロローグで始まらなければならない\r
-     * <li>エピローグで終わらなければならない\r
-     * <li>進行日はプロローグとエピローグに挟まれていなければならない\r
-     * </ul>\r
-     * @param list PeriodResource並び\r
-     * @throws IllegalArgumentException 引数が正当でない\r
-     */\r
-    public static void validatePeriodResource(List<PeriodResource> list)\r
-            throws IllegalArgumentException{\r
-        LandDef landDef = null;\r
-        int villageId = -1;\r
-        int lastDay = -1;\r
-        PeriodType periodType = null;\r
-\r
-        for(PeriodResource resource : list){\r
-            if(landDef == null){\r
-                landDef = resource.getLandDef();\r
-            }else if(resource.getLandDef() != landDef){\r
-                throw new IllegalArgumentException();\r
-            }\r
-\r
-            if(villageId < 0){\r
-                villageId = resource.getVillageId();\r
-            }else if(resource.getVillageId() != villageId){\r
-                throw new IllegalArgumentException();\r
-            }\r
-\r
-            if(lastDay < 0){\r
-                lastDay = resource.getDay();\r
-                if(lastDay != 0) throw new IllegalArgumentException();\r
-            }else{\r
-                if(resource.getDay() != lastDay + 1){\r
-                    throw new IllegalArgumentException();\r
-                }\r
-                lastDay = resource.getDay();\r
-            }\r
-\r
-            if(periodType == null){\r
-                periodType = resource.getPeriodType();\r
-                if(periodType != PeriodType.PROLOGUE){\r
-                    throw new IllegalArgumentException();\r
-                }\r
-                if(lastDay != 0) throw new IllegalArgumentException();\r
-            }else if(periodType == PeriodType.PROLOGUE){\r
-                periodType = resource.getPeriodType();\r
-                if(periodType != PeriodType.PROGRESS){\r
-                    throw new IllegalArgumentException();\r
-                }\r
-            }else if(periodType == PeriodType.PROGRESS){\r
-                periodType = resource.getPeriodType();\r
-            }else if(periodType == PeriodType.EPILOGUE){\r
-                throw new IllegalArgumentException();\r
-            }\r
-        }\r
-\r
-        if(lastDay < 0) throw new IllegalArgumentException();\r
-        if(periodType != PeriodType.EPILOGUE){\r
-            throw new IllegalArgumentException();\r
-        }\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 全PeriodResourceから、共通するベースURIを抽出する。\r
-     * @param list PeriodResource並び\r
-     * @return ベースURI文字列\r
-     * @throws IllegalArgumentException ベースURIが一致していない\r
-     */\r
-    public static String getBaseUri(List<PeriodResource> list)\r
-            throws IllegalArgumentException{\r
-        String result = null;\r
-\r
-        for(PeriodResource resource : list){\r
-            String urlText = resource.getOrigUrlText();\r
-            urlText = urlText.replaceAll("[^/]*$", "");\r
-            if(result == null){\r
-                result = urlText;\r
-            }else{\r
-                if( ! result.equals(urlText) ){\r
-                    throw new IllegalArgumentException();\r
-                }\r
-            }\r
-        }\r
-\r
-        return result;\r
-    }\r
-\r
-    /**\r
-     * 国情報を取得する。\r
-     * @return 国情報\r
-     */\r
-    public LandDef getLandDef(){\r
-        return this.landDef;\r
-    }\r
-\r
-    /**\r
-     * 村IDを取得する。\r
-     * @return 村ID\r
-     */\r
-    public int getVillageId(){\r
-        return this.villageId;\r
-    }\r
-\r
-    /**\r
-     * ベースURIを取得する。\r
-     * @return ベースURI\r
-     */\r
-    public String getBaseUri(){\r
-        return this.baseUri;\r
-    }\r
-\r
-    /**\r
-     * 村フルネームを取得する。\r
-     * @return 村フルネーム\r
-     */\r
-    public String getFullName(){\r
-        return this.fullName;\r
-    }\r
-\r
-    /**\r
-     * 村フルネームを設定する。\r
-     * @param fullName 村フルネーム\r
-     */\r
-    public void setFullName(String fullName){\r
-        this.fullName = fullName;\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 更新時を取得する。\r
-     * @return 更新時\r
-     */\r
-    public int getCommitHour(){\r
-        return this.commitHour;\r
-    }\r
-\r
-    /**\r
-     * 更新時を設定する。\r
-     * @param commitHour 更新時\r
-     */\r
-    public void setCommitHour(int commitHour){\r
-        this.commitHour = commitHour;\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 更新分を取得する。\r
-     * @return 更新分\r
-     */\r
-    public int getCommitMinute(){\r
-        return this.commitMinute;\r
-    }\r
-\r
-    /**\r
-     * 更新分を設定する。\r
-     * @param commitMinute 更新分\r
-     */\r
-    public void setCommitMinute(int commitMinute){\r
-        this.commitMinute = commitMinute;\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 墓アイコンURIを取得する。\r
-     * @return 墓アイコンURI文字列\r
-     */\r
-    public String getGraveIconUri(){\r
-        if(this.graveIconUri == null){\r
-            return this.landDef.getTombFaceIconURI().toASCIIString();\r
-        }\r
-        return this.graveIconUri;\r
-    }\r
-\r
-    /**\r
-     * 墓アイコンURI文字列を設定する。\r
-     * @param graveIconUri 墓アイコンURI文字列\r
-     */\r
-    public void setGraveIconUri(String graveIconUri){\r
-        this.graveIconUri = graveIconUri;\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 全Periodの開示状況から総合開示状況を算出する。\r
-     * @return 公開状況\r
-     */\r
-    public DisclosureType getDisclosureType(){\r
-        DisclosureType result = DisclosureType.COMPLETE;\r
-\r
-        for(PeriodData period : this.periodList){\r
-            DisclosureType type = period.getDisclosureType();\r
-            switch(type){\r
-            case HOT:\r
-                return DisclosureType.HOT;\r
-            case UNCOMPLETE:\r
-                result = DisclosureType.UNCOMPLETE;\r
-                break;\r
-            default:\r
-                break;\r
-            }\r
-        }\r
-\r
-        return result;\r
-    }\r
-\r
-    /**\r
-     * Periodモデルを追加する。\r
-     * @param period Periodモデル\r
-     */\r
-    public void addPeriodData(PeriodData period){\r
-        this.periodList.add(period);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * PeriodResourcenar並びを取得する。\r
-     * @return PeriodResource並び\r
-     */\r
-    public List<PeriodResource> getPeriodResourceList(){\r
-        return Collections.unmodifiableList(this.resourceList);\r
-    }\r
-\r
-    /**\r
-     * 未知の新規Avatarを生成する。\r
-     * ※ F1556村などへの対処。\r
-     * Avatarのフルネーム、短縮名、識別子が設定される。\r
-     * @param avfullName Avatarのフルネーム\r
-     * @return 新規Avatarモデル\r
-     */\r
-    public AvatarData createAvatar(String avfullName){\r
-        AvatarData avatar = new AvatarData();\r
-\r
-        avatar.setFullName(avfullName);\r
-\r
-        String[] token = avfullName.split("\\s");\r
-        String shortName = token[token.length - 1];\r
-        avatar.setShortName(shortName);\r
-\r
-        String avatarId = "ukavatar" + this.undefAvatarNo;\r
-        this.undefAvatarNo++;\r
-        avatar.setAvatarId(avatarId);\r
-\r
-        return avatar;\r
-    }\r
-\r
-    /**\r
-     * AvatarフルネームからAvatarを得る。\r
-     * まだこの村にいないAvatarならAvatar一覧に登録される。\r
-     * @param seq Avatarフルネーム\r
-     * @return Avatarモデル\r
-     */\r
-    public AvatarData getAvatarData(CharSequence seq){\r
-        for(AvatarData avatar : this.avatarList){\r
-            String avfullName = avatar.getFullName();\r
-            if(avfullName.contentEquals(seq)){\r
-                return avatar;\r
-            }\r
-        }\r
-\r
-        PreDefAvatar predefAvatar =\r
-                AvatarData.getPreDefAvatar(seq);\r
-        if(predefAvatar != null){\r
-            AvatarData avatar = new AvatarData(predefAvatar);\r
-            this.avatarList.add(avatar);\r
-            return avatar;\r
-        }\r
-\r
-        AvatarData avatar = createAvatar(seq.toString());\r
-        this.avatarList.add(avatar);\r
-\r
-        return avatar;\r
-    }\r
-\r
-    /**\r
-     * avatarList要素のXML出力。\r
-     * @param writer 出力先\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public void dumpAvatarList(Writer writer) throws IOException{\r
-        writer.append("<avatarList>").append("\n\n");\r
-\r
-        for(AvatarData avatar : this.avatarList){\r
-            avatar.dumpXml(writer);\r
-            writer.append('\n');\r
-        }\r
-\r
-        writer.append("</avatarList>").append('\n');\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 全period要素のXML出力。\r
-     * @param writer 出力先\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public void dumpPeriodList(Writer writer) throws IOException{\r
-        for(PeriodData period : this.periodList){\r
-            period.dumpXml(writer);\r
-            writer.append('\n');\r
-        }\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * village要素のXML出力。\r
-     * @param writer 出力先\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public void dumpXml(Writer writer) throws IOException{\r
-        writer.append("<village\n");\r
-\r
-        XmlUtils.indent(writer, 1);\r
-        XmlUtils.dumpNameSpaceDecl(writer);\r
-        writer.append('\n');\r
-\r
-        XmlUtils.indent(writer, 1);\r
-        XmlUtils.dumpSiNameSpaceDecl(writer);\r
-        writer.append('\n');\r
-\r
-        XmlUtils.indent(writer, 1);\r
-        XmlUtils.dumpSchemeLocation(writer);\r
-        writer.append('\n');\r
-\r
-        XmlUtils.indent(writer, 1);\r
-        XmlUtils.attrOut(writer, "xml:lang", "ja-JP");\r
-        writer.append('\n');\r
-\r
-        XmlUtils.indent(writer, 1);\r
-        XmlUtils.attrOut(writer, "xml:base", this.baseUri);\r
-        writer.append('\n');\r
-\r
-        XmlUtils.indent(writer, 1);\r
-        XmlUtils.attrOut(writer, "fullName", this.fullName);\r
-\r
-        writer.append(' ');\r
-        XmlUtils.attrOut(writer, "vid", Integer.toString(this.villageId));\r
-        writer.append('\n');\r
-\r
-        XmlUtils.indent(writer, 1);\r
-        XmlUtils.timeAttrOut(writer,\r
-                             "commitTime",\r
-                             this.commitHour, this.commitMinute);\r
-        writer.append('\n');\r
-\r
-        XmlUtils.indent(writer, 1);\r
-        XmlUtils.attrOut(writer, "state", "gameover");\r
-\r
-        DisclosureType type = getDisclosureType();\r
-        if(type != DisclosureType.COMPLETE){\r
-            writer.append(' ');\r
-            XmlUtils.attrOut(writer, "disclosure", type.getXmlName());\r
-        }\r
-\r
-        String isValid;\r
-        if(this.landDef.isValidVillageId(this.villageId)){\r
-            isValid = "true";\r
-        }else{\r
-            isValid = "false";\r
-        }\r
-        writer.append(' ');\r
-        XmlUtils.attrOut(writer, "isValid", isValid);\r
-        writer.append('\n');\r
-\r
-        XmlUtils.indent(writer, 1);\r
-        XmlUtils.attrOut(writer, "landName", this.landDef.getLandName());\r
-\r
-        writer.append(' ');\r
-        XmlUtils.attrOut(writer, "formalName", this.landDef.getFormalName());\r
-        writer.append('\n');\r
-\r
-        XmlUtils.indent(writer, 1);\r
-        XmlUtils.attrOut(writer, "landId", this.landDef.getLandId());\r
-\r
-        writer.append(' ');\r
-        XmlUtils.attrOut(writer, "landPrefix", this.landDef.getLandPrefix());\r
-        writer.append('\n');\r
-\r
-        XmlUtils.indent(writer, 1);\r
-        String locale = this.landDef.getLocale().toString();\r
-        locale = locale.replaceAll("_", "-");\r
-        XmlUtils.attrOut(writer, "locale", locale);\r
-\r
-        writer.append(' ');\r
-        XmlUtils.attrOut(writer,\r
-                "origencoding", this.landDef.getEncoding().name());\r
-\r
-        writer.append(' ');\r
-        XmlUtils.attrOut(writer,\r
-                "timezone", this.landDef.getTimeZone().getID());\r
-        writer.append('\n');\r
-\r
-        XmlUtils.indent(writer, 1);\r
-        XmlUtils.attrOut(writer, "graveIconURI", getGraveIconUri());\r
-        writer.append('\n');\r
-\r
-        XmlUtils.indent(writer, 1);\r
-        XmlUtils.attrOut(writer, "generator", JinArchiver.GENERATOR);\r
-        writer.append('\n');\r
-\r
-        writer.append(">").append('\n');\r
-\r
-        writer.append('\n');\r
-        dumpAvatarList(writer);\r
-\r
-        writer.append('\n');\r
-        dumpPeriodList(writer);\r
-\r
-        writer.append("</village>").append("\n");\r
-\r
-        return;\r
-    }\r
-\r
-}\r
+/*
+ * village data
+ *
+ * License : The MIT License
+ * Copyright(c) 2008 olyutorskii
+ */
+
+package jp.sourceforge.jindolf.archiver;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import jp.sourceforge.jindolf.corelib.DisclosureType;
+import jp.sourceforge.jindolf.corelib.LandDef;
+import jp.sourceforge.jindolf.corelib.PeriodType;
+import jp.sourceforge.jindolf.corelib.PreDefAvatar;
+
+/**
+ * 村のデータモデル。
+ * villageタグに相当。
+ */
+public class VillageData{
+
+    private final List<PeriodResource> resourceList;
+
+    private final LandDef landDef;
+    private final int villageId;
+    private final String baseUri;
+
+    private String fullName = "";
+    private int commitHour = -1;
+    private int commitMinute = -1;
+    private String graveIconUri;
+
+    private final List<AvatarData> avatarList = new LinkedList<AvatarData>();
+    private int undefAvatarNo = 1;
+
+    private final List<PeriodData> periodList = new LinkedList<PeriodData>();
+
+
+    /**
+     * コンストラクタ。
+     * @param resourceList PeriodResource並び
+     */
+    public VillageData(List<PeriodResource> resourceList){
+        super();
+
+        validatePeriodResource(resourceList);
+
+        this.resourceList = new LinkedList<PeriodResource>(resourceList);
+
+        PeriodResource resource1st = this.resourceList.get(0);
+        this.landDef   = resource1st.getLandDef();
+        this.villageId = resource1st.getVillageId();
+        this.baseUri = getBaseUri(this.resourceList);
+
+        return;
+    }
+
+
+    /**
+     * PeriodResourceの組が正当かチェックする。
+     * <ul>
+     * <li>全て同じ国に属していなければならない
+     * <li>全て同じ村に属していなければならない
+     * <li>日付は0から始まる連続した数値でなければならない
+     * <li>プロローグで始まらなければならない
+     * <li>エピローグで終わらなければならない
+     * <li>進行日はプロローグとエピローグに挟まれていなければならない
+     * </ul>
+     * @param list PeriodResource並び
+     * @throws IllegalArgumentException 引数が正当でない
+     */
+    public static void validatePeriodResource(List<PeriodResource> list)
+            throws IllegalArgumentException{
+        LandDef landDef = null;
+        int villageId = -1;
+        int lastDay = -1;
+        PeriodType periodType = null;
+
+        for(PeriodResource resource : list){
+            if(landDef == null){
+                landDef = resource.getLandDef();
+            }else if(resource.getLandDef() != landDef){
+                throw new IllegalArgumentException();
+            }
+
+            if(villageId < 0){
+                villageId = resource.getVillageId();
+            }else if(resource.getVillageId() != villageId){
+                throw new IllegalArgumentException();
+            }
+
+            if(lastDay < 0){
+                lastDay = resource.getDay();
+                if(lastDay != 0) throw new IllegalArgumentException();
+            }else{
+                if(resource.getDay() != lastDay + 1){
+                    throw new IllegalArgumentException();
+                }
+                lastDay = resource.getDay();
+            }
+
+            if(periodType == null){
+                periodType = resource.getPeriodType();
+                if(periodType != PeriodType.PROLOGUE){
+                    throw new IllegalArgumentException();
+                }
+                if(lastDay != 0) throw new IllegalArgumentException();
+            }else if(periodType == PeriodType.PROLOGUE){
+                periodType = resource.getPeriodType();
+                if(periodType != PeriodType.PROGRESS){
+                    throw new IllegalArgumentException();
+                }
+            }else if(periodType == PeriodType.PROGRESS){
+                periodType = resource.getPeriodType();
+            }else if(periodType == PeriodType.EPILOGUE){
+                throw new IllegalArgumentException();
+            }
+        }
+
+        if(lastDay < 0) throw new IllegalArgumentException();
+        if(periodType != PeriodType.EPILOGUE){
+            throw new IllegalArgumentException();
+        }
+
+        return;
+    }
+
+    /**
+     * 全PeriodResourceから、共通するベースURIを抽出する。
+     * @param list PeriodResource並び
+     * @return ベースURI文字列
+     * @throws IllegalArgumentException ベースURIが一致していない
+     */
+    public static String getBaseUri(List<PeriodResource> list)
+            throws IllegalArgumentException{
+        String result = null;
+
+        for(PeriodResource resource : list){
+            String urlText = resource.getOrigUrlText();
+            urlText = urlText.replaceAll("[^/]*$", "");
+            if(result == null){
+                result = urlText;
+            }else{
+                if( ! result.equals(urlText) ){
+                    throw new IllegalArgumentException();
+                }
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * 国情報を取得する。
+     * @return 国情報
+     */
+    public LandDef getLandDef(){
+        return this.landDef;
+    }
+
+    /**
+     * 村IDを取得する。
+     * @return 村ID
+     */
+    public int getVillageId(){
+        return this.villageId;
+    }
+
+    /**
+     * ベースURIを取得する。
+     * @return ベースURI
+     */
+    public String getBaseUri(){
+        return this.baseUri;
+    }
+
+    /**
+     * 村フルネームを取得する。
+     * @return 村フルネーム
+     */
+    public String getFullName(){
+        return this.fullName;
+    }
+
+    /**
+     * 村フルネームを設定する。
+     * @param fullName 村フルネーム
+     */
+    public void setFullName(String fullName){
+        this.fullName = fullName;
+        return;
+    }
+
+    /**
+     * 更新時を取得する。
+     * @return 更新時
+     */
+    public int getCommitHour(){
+        return this.commitHour;
+    }
+
+    /**
+     * 更新時を設定する。
+     * @param commitHour 更新時
+     */
+    public void setCommitHour(int commitHour){
+        this.commitHour = commitHour;
+        return;
+    }
+
+    /**
+     * 更新分を取得する。
+     * @return 更新分
+     */
+    public int getCommitMinute(){
+        return this.commitMinute;
+    }
+
+    /**
+     * 更新分を設定する。
+     * @param commitMinute 更新分
+     */
+    public void setCommitMinute(int commitMinute){
+        this.commitMinute = commitMinute;
+        return;
+    }
+
+    /**
+     * 墓アイコンURIを取得する。
+     * @return 墓アイコンURI文字列
+     */
+    public String getGraveIconUri(){
+        if(this.graveIconUri == null){
+            return this.landDef.getTombFaceIconURI().toASCIIString();
+        }
+        return this.graveIconUri;
+    }
+
+    /**
+     * 墓アイコンURI文字列を設定する。
+     * @param graveIconUri 墓アイコンURI文字列
+     */
+    public void setGraveIconUri(String graveIconUri){
+        this.graveIconUri = graveIconUri;
+        return;
+    }
+
+    /**
+     * 全Periodの開示状況から総合開示状況を算出する。
+     * @return 公開状況
+     */
+    public DisclosureType getDisclosureType(){
+        DisclosureType result = DisclosureType.COMPLETE;
+
+        for(PeriodData period : this.periodList){
+            DisclosureType type = period.getDisclosureType();
+            switch(type){
+            case HOT:
+                return DisclosureType.HOT;
+            case UNCOMPLETE:
+                result = DisclosureType.UNCOMPLETE;
+                break;
+            default:
+                break;
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Periodモデルを追加する。
+     * @param period Periodモデル
+     */
+    public void addPeriodData(PeriodData period){
+        this.periodList.add(period);
+        return;
+    }
+
+    /**
+     * PeriodResourcenar並びを取得する。
+     * @return PeriodResource並び
+     */
+    public List<PeriodResource> getPeriodResourceList(){
+        return Collections.unmodifiableList(this.resourceList);
+    }
+
+    /**
+     * 未知の新規Avatarを生成する。
+     * ※ F1556村などへの対処。
+     * Avatarのフルネーム、短縮名、識別子が設定される。
+     * @param avfullName Avatarのフルネーム
+     * @return 新規Avatarモデル
+     */
+    public AvatarData createAvatar(String avfullName){
+        AvatarData avatar = new AvatarData();
+
+        avatar.setFullName(avfullName);
+
+        String[] token = avfullName.split("\\s");
+        String shortName = token[token.length - 1];
+        avatar.setShortName(shortName);
+
+        String avatarId = "ukavatar" + this.undefAvatarNo;
+        this.undefAvatarNo++;
+        avatar.setAvatarId(avatarId);
+
+        return avatar;
+    }
+
+    /**
+     * AvatarフルネームからAvatarを得る。
+     * まだこの村にいないAvatarならAvatar一覧に登録される。
+     * @param seq Avatarフルネーム
+     * @return Avatarモデル
+     */
+    public AvatarData getAvatarData(CharSequence seq){
+        for(AvatarData avatar : this.avatarList){
+            String avfullName = avatar.getFullName();
+            if(avfullName.contentEquals(seq)){
+                return avatar;
+            }
+        }
+
+        PreDefAvatar predefAvatar =
+                AvatarData.getPreDefAvatar(seq);
+        if(predefAvatar != null){
+            AvatarData avatar = new AvatarData(predefAvatar);
+            this.avatarList.add(avatar);
+            return avatar;
+        }
+
+        AvatarData avatar = createAvatar(seq.toString());
+        this.avatarList.add(avatar);
+
+        return avatar;
+    }
+
+    /**
+     * avatarList要素のXML出力。
+     * @param writer 出力先
+     * @throws IOException 出力エラー
+     */
+    public void dumpAvatarList(Writer writer) throws IOException{
+        writer.append("<avatarList>").append("\n\n");
+
+        for(AvatarData avatar : this.avatarList){
+            avatar.dumpXml(writer);
+            writer.append('\n');
+        }
+
+        writer.append("</avatarList>").append('\n');
+
+        return;
+    }
+
+    /**
+     * 全period要素のXML出力。
+     * @param writer 出力先
+     * @throws IOException 出力エラー
+     */
+    public void dumpPeriodList(Writer writer) throws IOException{
+        for(PeriodData period : this.periodList){
+            period.dumpXml(writer);
+            writer.append('\n');
+        }
+        return;
+    }
+
+    /**
+     * village要素のXML出力。
+     * @param writer 出力先
+     * @throws IOException 出力エラー
+     */
+    public void dumpXml(Writer writer) throws IOException{
+        writer.append("<village\n");
+
+        XmlUtils.indent(writer, 1);
+        XmlUtils.dumpNameSpaceDecl(writer);
+        writer.append('\n');
+
+        XmlUtils.indent(writer, 1);
+        XmlUtils.dumpSiNameSpaceDecl(writer);
+        writer.append('\n');
+
+        XmlUtils.indent(writer, 1);
+        XmlUtils.dumpSchemeLocation(writer);
+        writer.append('\n');
+
+        XmlUtils.indent(writer, 1);
+        XmlUtils.attrOut(writer, "xml:lang", "ja-JP");
+        writer.append('\n');
+
+        XmlUtils.indent(writer, 1);
+        XmlUtils.attrOut(writer, "xml:base", this.baseUri);
+        writer.append('\n');
+
+        XmlUtils.indent(writer, 1);
+        XmlUtils.attrOut(writer, "fullName", this.fullName);
+
+        writer.append(' ');
+        XmlUtils.attrOut(writer, "vid", Integer.toString(this.villageId));
+        writer.append('\n');
+
+        XmlUtils.indent(writer, 1);
+        XmlUtils.timeAttrOut(writer,
+                             "commitTime",
+                             this.commitHour, this.commitMinute);
+        writer.append('\n');
+
+        XmlUtils.indent(writer, 1);
+        XmlUtils.attrOut(writer, "state", "gameover");
+
+        DisclosureType type = getDisclosureType();
+        if(type != DisclosureType.COMPLETE){
+            writer.append(' ');
+            XmlUtils.attrOut(writer, "disclosure", type.getXmlName());
+        }
+
+        String isValid;
+        if(this.landDef.isValidVillageId(this.villageId)){
+            isValid = "true";
+        }else{
+            isValid = "false";
+        }
+        writer.append(' ');
+        XmlUtils.attrOut(writer, "isValid", isValid);
+        writer.append('\n');
+
+        XmlUtils.indent(writer, 1);
+        XmlUtils.attrOut(writer, "landName", this.landDef.getLandName());
+
+        writer.append(' ');
+        XmlUtils.attrOut(writer, "formalName", this.landDef.getFormalName());
+        writer.append('\n');
+
+        XmlUtils.indent(writer, 1);
+        XmlUtils.attrOut(writer, "landId", this.landDef.getLandId());
+
+        writer.append(' ');
+        XmlUtils.attrOut(writer, "landPrefix", this.landDef.getLandPrefix());
+        writer.append('\n');
+
+        XmlUtils.indent(writer, 1);
+        String locale = this.landDef.getLocale().toString();
+        locale = locale.replaceAll("_", "-");
+        XmlUtils.attrOut(writer, "locale", locale);
+
+        writer.append(' ');
+        XmlUtils.attrOut(writer,
+                "origencoding", this.landDef.getEncoding().name());
+
+        writer.append(' ');
+        XmlUtils.attrOut(writer,
+                "timezone", this.landDef.getTimeZone().getID());
+        writer.append('\n');
+
+        XmlUtils.indent(writer, 1);
+        XmlUtils.attrOut(writer, "graveIconURI", getGraveIconUri());
+        writer.append('\n');
+
+        XmlUtils.indent(writer, 1);
+        XmlUtils.attrOut(writer, "generator", JinArchiver.GENERATOR);
+        writer.append('\n');
+
+        writer.append(">").append('\n');
+
+        writer.append('\n');
+        dumpAvatarList(writer);
+
+        writer.append('\n');
+        dumpPeriodList(writer);
+
+        writer.append("</village>").append("\n");
+
+        return;
+    }
+
+}
index 07b23b1..f08c75d 100644 (file)
-/*\r
- * windows-31j encoding utilities\r
- *\r
- * License : The MIT License\r
- * Copyright(c) 2008 olyutorskii\r
- */\r
-\r
-package jp.sourceforge.jindolf.archiver;\r
-\r
-import java.nio.ByteBuffer;\r
-import java.nio.charset.CharacterCodingException;\r
-import java.nio.charset.Charset;\r
-import java.nio.charset.CharsetDecoder;\r
-import java.nio.charset.CodingErrorAction;\r
-import jp.sourceforge.jindolf.parser.DecodeErrorInfo;\r
-import jp.sourceforge.jindolf.parser.DecodedContent;\r
-\r
-/**\r
- * windows-31jエンコーディング(機種依存文字)に関する諸々。\r
- */\r
-public final class Win31j{\r
-\r
-    /** デフォルト置換文字。 */\r
-    public static final char REP_CHAR = '\ufffd';\r
-\r
-    /** windows-31j Charset. */\r
-    public static final Charset CS_WIN31J = Charset.forName("windows-31j");\r
-\r
-    private static final CharsetDecoder WIN31JDECODER;\r
-    private static final ByteBuffer BYTEBUFFER = ByteBuffer.allocate(2);\r
-\r
-    static{\r
-        WIN31JDECODER = CS_WIN31J.newDecoder();\r
-        WIN31JDECODER.onMalformedInput(CodingErrorAction.REPORT);\r
-        WIN31JDECODER.onUnmappableCharacter(CodingErrorAction.REPORT);\r
-        WIN31JDECODER.reset();\r
-        BYTEBUFFER.clear();\r
-    }\r
-\r
-\r
-    /**\r
-     * 隠しコンストラクタ。\r
-     */\r
-    private Win31j(){\r
-        throw new Error();\r
-    }\r
-\r
-\r
-    /**\r
-     * winsows-31jエンコーディングされたと想定した2バイトデータ\r
-     * の復号を試みる。\r
-     * 復号に失敗すればU+FFFDを返す。\r
-     * @param b1 1バイト目\r
-     * @param b2 2バイト目\r
-     * @return 復号化された1文字\r
-     */\r
-    public static synchronized char getWin31jChar(byte b1, byte b2){\r
-        char replaced;\r
-\r
-        WIN31JDECODER.reset();\r
-        BYTEBUFFER.clear();\r
-        BYTEBUFFER.put(b1).put(b2);\r
-        BYTEBUFFER.flip();\r
-\r
-        try{\r
-            replaced = WIN31JDECODER.decode(BYTEBUFFER).charAt(0);\r
-        }catch(CharacterCodingException e){\r
-            replaced = REP_CHAR;\r
-        }\r
-\r
-        return replaced;\r
-    }\r
-\r
-    /**\r
-     * デコードエラーがwindows-31jに由来する物と仮定して\r
-     * 復号を試みる。\r
-     * 1バイトエラーもしくは復号に失敗すればU+FFFDを返す。\r
-     * @param info デコードエラー\r
-     * @return 復号化された1文字\r
-     */\r
-    public static char getWin31jChar(DecodeErrorInfo info){\r
-        if( ! info.has2nd() ) return REP_CHAR;\r
-\r
-        byte b1 = info.getRawByte1st();\r
-        byte b2 = info.getRawByte2nd();\r
-        char replaceChar = getWin31jChar(b1, b2);\r
-\r
-        return replaceChar;\r
-    }\r
-\r
-    /**\r
-     * デコードエラーを含む文字列に対し、\r
-     * windows-31jによる復号での補完を試みる。\r
-     * @param content 文字列\r
-     */\r
-    public static void supplyWin31jChar(DecodedContent content){\r
-        if( ! content.hasDecodeError() ) return;\r
-\r
-        for(DecodeErrorInfo info : content.getDecodeErrorList()){\r
-            int pos = info.getCharPosition();\r
-            char replaceChar = getWin31jChar(info);\r
-            content.setCharAt(pos, replaceChar);\r
-        }\r
-\r
-        return;\r
-    }\r
-\r
-}\r
+/*
+ * windows-31j encoding utilities
+ *
+ * License : The MIT License
+ * Copyright(c) 2008 olyutorskii
+ */
+
+package jp.sourceforge.jindolf.archiver;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CodingErrorAction;
+import jp.sourceforge.jindolf.parser.DecodeErrorInfo;
+import jp.sourceforge.jindolf.parser.DecodedContent;
+
+/**
+ * windows-31jエンコーディング(機種依存文字)に関する諸々。
+ */
+public final class Win31j{
+
+    /** デフォルト置換文字。 */
+    public static final char REP_CHAR = '\ufffd';
+
+    /** windows-31j Charset. */
+    public static final Charset CS_WIN31J = Charset.forName("windows-31j");
+
+    private static final CharsetDecoder WIN31JDECODER;
+    private static final ByteBuffer BYTEBUFFER = ByteBuffer.allocate(2);
+
+    static{
+        WIN31JDECODER = CS_WIN31J.newDecoder();
+        WIN31JDECODER.onMalformedInput(CodingErrorAction.REPORT);
+        WIN31JDECODER.onUnmappableCharacter(CodingErrorAction.REPORT);
+        WIN31JDECODER.reset();
+        BYTEBUFFER.clear();
+    }
+
+
+    /**
+     * 隠しコンストラクタ。
+     */
+    private Win31j(){
+        throw new Error();
+    }
+
+
+    /**
+     * winsows-31jエンコーディングされたと想定した2バイトデータ
+     * の復号を試みる。
+     * 復号に失敗すればU+FFFDを返す。
+     * @param b1 1バイト目
+     * @param b2 2バイト目
+     * @return 復号化された1文字
+     */
+    public static synchronized char getWin31jChar(byte b1, byte b2){
+        char replaced;
+
+        WIN31JDECODER.reset();
+        BYTEBUFFER.clear();
+        BYTEBUFFER.put(b1).put(b2);
+        BYTEBUFFER.flip();
+
+        try{
+            replaced = WIN31JDECODER.decode(BYTEBUFFER).charAt(0);
+        }catch(CharacterCodingException e){
+            replaced = REP_CHAR;
+        }
+
+        return replaced;
+    }
+
+    /**
+     * デコードエラーがwindows-31jに由来する物と仮定して
+     * 復号を試みる。
+     * 1バイトエラーもしくは復号に失敗すればU+FFFDを返す。
+     * @param info デコードエラー
+     * @return 復号化された1文字
+     */
+    public static char getWin31jChar(DecodeErrorInfo info){
+        if( ! info.has2nd() ) return REP_CHAR;
+
+        byte b1 = info.getRawByte1st();
+        byte b2 = info.getRawByte2nd();
+        char replaceChar = getWin31jChar(b1, b2);
+
+        return replaceChar;
+    }
+
+    /**
+     * デコードエラーを含む文字列に対し、
+     * windows-31jによる復号での補完を試みる。
+     * @param content 文字列
+     */
+    public static void supplyWin31jChar(DecodedContent content){
+        if( ! content.hasDecodeError() ) return;
+
+        for(DecodeErrorInfo info : content.getDecodeErrorList()){
+            int pos = info.getCharPosition();
+            char replaceChar = getWin31jChar(info);
+            content.setCharAt(pos, replaceChar);
+        }
+
+        return;
+    }
+
+}
index b8b8d65..afc87ad 100644 (file)
-/*\r
- * XML utils\r
- *\r
- * License : The MIT License\r
- * Copyright(c) 2008 olyutorskii\r
- */\r
-\r
-package jp.sourceforge.jindolf.archiver;\r
-\r
-import java.io.BufferedOutputStream;\r
-import java.io.BufferedWriter;\r
-import java.io.File;\r
-import java.io.FileOutputStream;\r
-import java.io.IOException;\r
-import java.io.OutputStream;\r
-import java.io.OutputStreamWriter;\r
-import java.io.Writer;\r
-import java.text.MessageFormat;\r
-import java.util.Calendar;\r
-import java.util.GregorianCalendar;\r
-import java.util.List;\r
-import java.util.TimeZone;\r
-import jp.sourceforge.jindolf.corelib.LandDef;\r
-import jp.sourceforge.jindolf.parser.DecodeErrorInfo;\r
-import jp.sourceforge.jindolf.parser.DecodedContent;\r
-\r
-/**\r
- * XML用各種ユーティリティ。\r
- */\r
-public final class XmlUtils{\r
-\r
-    private static final String ORIG_DTD =\r
-            "http://jindolf.sourceforge.jp/xml/dtd/bbsArchive-110421.dtd";\r
-    private static final String ORIG_NS =\r
-            "http://jindolf.sourceforge.jp/xml/ns/501";\r
-    private static final String ORIG_SCHEME =\r
-            "http://jindolf.sourceforge.jp/xml/xsd/bbsArchive-110421.xsd";\r
-    private static final String SCHEMA_NS =\r
-            "http://www.w3.org/2001/XMLSchema-instance";\r
-\r
-    private static final String OUTPATH = "D:\\TEMP\\zxzx\\";\r
-\r
-    private static final char BS_CHAR = (char) 0x005c; // Backslash\r
-    private static final String INDENT_UNIT = "\u0020\u0020";\r
-\r
-    private static final TimeZone TZ_TOKYO =\r
-            TimeZone.getTimeZone("Asia/Tokyo");\r
-\r
-\r
-    /**\r
-     * 隠れコンストラクタ。\r
-     */\r
-    private XmlUtils(){\r
-        throw new Error();\r
-    }\r
-\r
-\r
-    /**\r
-     * DOCTYPE宣言を出力する。\r
-     * @param writer 出力先\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public static void dumpDocType(Writer writer) throws IOException{\r
-        writer.append("<!DOCTYPE village SYSTEM ");\r
-        writer.append('"');\r
-        writer.append(ORIG_DTD);\r
-        writer.append('"');\r
-        writer.append(" >");\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * オリジナルNameSpace宣言を出力する。\r
-     * @param writer 出力先\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public static void dumpNameSpaceDecl(Writer writer)\r
-            throws IOException{\r
-        attrOut(writer, "xmlns", ORIG_NS);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * スキーマNameSpace宣言を出力する。\r
-     * @param writer 出力先\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public static void dumpSiNameSpaceDecl(Writer writer)\r
-            throws IOException{\r
-        attrOut(writer, "xmlns:xsi", SCHEMA_NS);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * スキーマ位置指定を出力する。\r
-     * @param writer 出力先\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public static void dumpSchemeLocation(Writer writer)\r
-            throws IOException{\r
-        attrOut(writer,\r
-                "xsi:schemaLocation",\r
-                ORIG_NS + " " + ORIG_SCHEME);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * インデント用空白を出力する。\r
-     * ネスト単位は空白2文字\r
-     * @param writer 出力先\r
-     * @param level ネストレベル\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public static void indent(Writer writer, int level) throws IOException{\r
-        for(int ct = 1; ct <= level; ct++){\r
-            writer.append(INDENT_UNIT);\r
-        }\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * XML数値文字参照を出力する。\r
-     * @param writer 出力先\r
-     * @param chVal 出力文字\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public static void charRefOut(Writer writer, char chVal)\r
-            throws IOException{\r
-        if(chVal == '\u0020'){\r
-            writer.append("&#x20;");\r
-            return;\r
-        }\r
-\r
-        if(chVal == '\u0009'){\r
-            writer.append("&#x09;");\r
-            return;\r
-        }\r
-\r
-        int ival = 0xffff & ((int) chVal);\r
-        String hex = Integer.toHexString(ival);\r
-        if(hex.length() % 2 != 0) hex = "0" + hex;\r
-\r
-        writer.append("&#x");\r
-        writer.append(hex);\r
-        writer.append(";");\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 不正文字をXML出力する。\r
-     * @param writer 出力先\r
-     * @param chVal 不正文字\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public static void dumpInvalidChar(Writer writer, char chVal)\r
-            throws IOException{\r
-        int hexVal;\r
-        hexVal = chVal & 0xff;\r
-        String hexBin = Integer.toHexString(hexVal);\r
-        if(hexBin.length() % 2 != 0) hexBin = "0" + hexBin;\r
-\r
-        char replaceChar = '\ufffd';\r
-        if('\u0000' <= chVal && chVal <= '\u001f'){\r
-            replaceChar = (char)( chVal + '\u2400' );\r
-        }\r
-\r
-        writer.append("<rawdata");\r
-\r
-        writer.append(' ');\r
-        attrOut(writer, "encoding", "Shift_JIS");\r
-\r
-        writer.append(' ');\r
-        attrOut(writer, "hexBin", hexBin);\r
-\r
-        writer.append(" >");\r
-        writer.append(replaceChar);\r
-        writer.append("</rawdata>");\r
-    }\r
-\r
-    /**\r
-     * 任意の文字がXML規格上のホワイトスペースに属するか判定する。\r
-     * @param chVal 文字\r
-     * @return ホワイトスペースならtrue\r
-     */\r
-    public static boolean isWhiteSpace(char chVal){\r
-        switch(chVal){\r
-        case '\u0020':\r
-        case '\t':\r
-        case '\n':\r
-        case '\r':\r
-            return true;\r
-        default:\r
-            break;\r
-        }\r
-\r
-        return false;\r
-    }\r
-\r
-    /**\r
-     * 文字列を出力する。\r
-     * <ul>\r
-     * <li>先頭および末尾のホワイトスペースは強制的に文字参照化される。\r
-     * <li>連続したホワイトスペースの2文字目以降は文字参照化される。\r
-     * <li>スペースでないホワイトスペースは無条件に文字参照化される。\r
-     * <li>{@literal &, <, >, "}は無条件に文字参照化される。\r
-     * </ul>\r
-     * 参考:XML 1.0 規格 3.3.3節\r
-     * @param writer 出力先\r
-     * @param seq CDATA文字列\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public static void textOut(Writer writer, CharSequence seq)\r
-            throws IOException{\r
-        int len = seq.length();\r
-\r
-        boolean leadSpace = false;\r
-\r
-        for(int pos = 0; pos < len; pos++){\r
-            char chVal = seq.charAt(pos);\r
-\r
-            if(isWhiteSpace(chVal)){\r
-                if(pos == 0 || pos >= len - 1 || leadSpace){\r
-                    charRefOut(writer, chVal);\r
-                }else if(chVal != '\u0020'){\r
-                    charRefOut(writer, chVal);\r
-                }else{\r
-                    writer.append(chVal);\r
-                }\r
-                leadSpace = true;\r
-            }else{\r
-                if(chVal == '&'){\r
-                    writer.append("&amp;");\r
-                }else if(chVal == '<'){\r
-                    writer.append("&lt;");\r
-                }else if(chVal == '>'){\r
-                    writer.append("&gt;");\r
-                }else if(chVal == '"'){\r
-                    writer.append("&quot;");\r
-                }else if(chVal == '\''){\r
-                    writer.append("&apos;");\r
-                }else if(chVal == BS_CHAR){\r
-                    writer.append('\u00a5');\r
-                }else if(chVal == '\u007e'){\r
-                    writer.append('\u203e');\r
-                }else if(Character.isISOControl(chVal)){\r
-                    dumpInvalidChar(writer, chVal);\r
-                }else{\r
-                    writer.append(chVal);\r
-                }\r
-                leadSpace = false;\r
-            }\r
-        }\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 属性を出力する。\r
-     * @param writer 出力先\r
-     * @param name 属性名\r
-     * @param value 属性値\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public static void attrOut(Writer writer,\r
-                                CharSequence name,\r
-                                CharSequence value)\r
-            throws IOException{\r
-        StringBuilder newValue = new StringBuilder(value);\r
-        for(int pt = 0; pt < newValue.length(); pt++){\r
-            char chVal = newValue.charAt(pt);\r
-            if(chVal == '\n' || chVal == '\r' || chVal == '\t') continue;\r
-            if(Character.isISOControl(chVal)){\r
-                newValue.setCharAt(pt, (char)('\u2400' + chVal));\r
-            }\r
-        }\r
-\r
-        writer.append(name);\r
-        writer.append('=');\r
-        writer.append('"');\r
-        textOut(writer, newValue);\r
-        writer.append('"');\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * xsd:time形式の時刻属性を出力する。\r
-     * タイムゾーンは「+09:00」固定\r
-     * @param writer 出力先\r
-     * @param name 属性名\r
-     * @param hour 時間\r
-     * @param minute 分\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public static void timeAttrOut(Writer writer,\r
-                                     CharSequence name,\r
-                                     int hour, int minute)\r
-            throws IOException{\r
-        String cmtTime =\r
-                MessageFormat\r
-                .format("{0,number,#00}:{1,number,#00}:00+09:00",\r
-                        hour, minute);\r
-        attrOut(writer, name, cmtTime);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * xsd:gMonthDay形式の日付属性を出力する。\r
-     * タイムゾーンは「+09:00」固定\r
-     * @param writer 出力先\r
-     * @param name 属性名\r
-     * @param month 月\r
-     * @param day 日\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public static void dateAttrOut(Writer writer,\r
-                                     CharSequence name,\r
-                                     int month, int day)\r
-            throws IOException{\r
-        String dateAttr =\r
-                MessageFormat.format("--{0,number,#00}-{1,number,#00}+09:00",\r
-                                     month, day);\r
-        attrOut(writer, name, dateAttr);\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * xsd:dateTime形式の日付時刻属性を出力する。\r
-     * タイムゾーンは「+09:00」固定\r
-     * @param writer 出力先\r
-     * @param name 属性名\r
-     * @param epochMs エポック時刻\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public static void dateTimeAttr(Writer writer,\r
-                                      CharSequence name,\r
-                                      long epochMs)\r
-            throws IOException{\r
-        Calendar calendar = new GregorianCalendar(TZ_TOKYO);\r
-\r
-        calendar.setTimeInMillis(epochMs);\r
-        int year = calendar.get(Calendar.YEAR);\r
-        int month = calendar.get(Calendar.MONTH) + 1;\r
-        int day = calendar.get(Calendar.DATE);\r
-        int hour = calendar.get(Calendar.HOUR_OF_DAY);\r
-        int minute = calendar.get(Calendar.MINUTE);\r
-        int sec = calendar.get(Calendar.SECOND);\r
-        int msec = calendar.get(Calendar.MILLISECOND);\r
-\r
-        String attrVal = MessageFormat.format(\r
-                 "{0,number,#0000}-{1,number,#00}-{2,number,#00}"\r
-                +"T{3,number,#00}:{4,number,#00}:{5,number,#00}"\r
-                +".{6,number,#000}+09:00",\r
-                year, month, day, hour, minute, sec, msec);\r
-\r
-        attrOut(writer, name, attrVal);\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * デコードエラー情報をrawdataタグで出力する。\r
-     * 文字列集合に関するエラーの場合、windows31jでのデコード出力を試みる。\r
-     * @param writer 出力先\r
-     * @param errorInfo デコードエラー\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public static void dumpErrorInfo(Writer writer,\r
-                                       DecodeErrorInfo errorInfo)\r
-            throws IOException{\r
-        int hexVal;\r
-        hexVal = errorInfo.getRawByte1st() & 0xff;\r
-        if(errorInfo.has2nd()){\r
-            hexVal <<= 8;\r
-            hexVal |= errorInfo.getRawByte2nd() & 0xff;\r
-        }\r
-\r
-        String hexBin = Integer.toHexString(hexVal);\r
-        if(hexBin.length() % 2 != 0) hexBin = "0" + hexBin;\r
-\r
-        char replaceChar = Win31j.getWin31jChar(errorInfo);\r
-\r
-        writer.append("<rawdata");\r
-\r
-        writer.append(' ');\r
-        attrOut(writer, "encoding", "Shift_JIS");\r
-\r
-        writer.append(' ');\r
-        attrOut(writer, "hexBin", hexBin);\r
-\r
-        writer.append(" >");\r
-        writer.append(replaceChar);\r
-        writer.append("</rawdata>");\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * デコードエラー込みのテキストを出力する。\r
-     * @param writer 出力先\r
-     * @param content テキスト\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public static void dumpDecodedContent(Writer writer,\r
-                                             DecodedContent content)\r
-            throws IOException{\r
-        if( ! content.hasDecodeError() ){\r
-            textOut(writer, content);\r
-            return;\r
-        }\r
-\r
-        int last = 0;\r
-\r
-        List<DecodeErrorInfo> errList = content.getDecodeErrorList();\r
-        for(DecodeErrorInfo err : errList){\r
-            int charPos = err.getCharPosition();\r
-            CharSequence line = content.subSequence(last, charPos);\r
-            textOut(writer, line);\r
-            dumpErrorInfo(writer, err);\r
-            last = charPos + 1;\r
-        }\r
-\r
-        CharSequence line = content.subSequence(last, content.length());\r
-        textOut(writer, line);\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 村情報をXML形式で出力する。\r
-     * @param writer 出力先\r
-     * @param villageData 村情報\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public static void dumpVillageData(Writer writer,\r
-                                         VillageData villageData)\r
-            throws IOException{\r
-        writer.append("<?xml");\r
-        writer.append(' ');\r
-        attrOut(writer, "version", "1.0");\r
-        writer.append(' ');\r
-        attrOut(writer, "encoding", "UTF-8");\r
-        writer.append(" ?>\n\n");\r
-\r
-        writer.append("<!--\n");\r
-        writer.append("  人狼BBSアーカイブ\n");\r
-        writer.append("  http://jindolf.sourceforge.jp/\n");\r
-        writer.append("-->\n\n");\r
-\r
-        dumpDocType(writer);\r
-        writer.append("\n\n");\r
-\r
-        villageData.dumpXml(writer);\r
-\r
-        writer.append("\n<!-- EOF -->\n");\r
-\r
-        writer.flush();\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * 村情報を反映した出力ファイル名を生成する。\r
-     * @param village 村情報\r
-     * @return XML出力ファイル名\r
-     */\r
-    public static String createOutFileName(VillageData village){\r
-        LandDef landDef = village.getLandDef();\r
-        String landId = landDef.getLandId();\r
-        int vid = village.getVillageId();\r
-\r
-        String fname =\r
-                MessageFormat.format(\r
-                "{0}jin_{1}_{2,number,#00000}.xml", OUTPATH, landId, vid);\r
-        return fname;\r
-    }\r
-\r
-    /**\r
-     * 村情報を反映した出力ファイルへの文字ストリームを生成する。\r
-     * @param village 村情報\r
-     * @return 出力先文字ストリーム\r
-     * @throws IOException 出力エラー\r
-     */\r
-    public static Writer createFileWriter(VillageData village)\r
-            throws IOException{\r
-        String fname = createOutFileName(village);\r
-        File file = new File(fname);\r
-\r
-        OutputStream ostream;\r
-        ostream = new FileOutputStream(file);\r
-        ostream = new BufferedOutputStream(ostream, 10000);\r
-        Writer writer;\r
-        writer = new OutputStreamWriter(ostream, "UTF-8");\r
-        writer = new BufferedWriter(writer, 10000);\r
-        return writer;\r
-    }\r
-\r
-}\r
+/*
+ * XML utils
+ *
+ * License : The MIT License
+ * Copyright(c) 2008 olyutorskii
+ */
+
+package jp.sourceforge.jindolf.archiver;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.text.MessageFormat;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.List;
+import java.util.TimeZone;
+import jp.sourceforge.jindolf.corelib.LandDef;
+import jp.sourceforge.jindolf.parser.DecodeErrorInfo;
+import jp.sourceforge.jindolf.parser.DecodedContent;
+
+/**
+ * XML用各種ユーティリティ。
+ */
+public final class XmlUtils{
+
+    private static final String ORIG_DTD =
+            "http://jindolf.sourceforge.jp/xml/dtd/bbsArchive-110421.dtd";
+    private static final String ORIG_NS =
+            "http://jindolf.sourceforge.jp/xml/ns/501";
+    private static final String ORIG_SCHEME =
+            "http://jindolf.sourceforge.jp/xml/xsd/bbsArchive-110421.xsd";
+    private static final String SCHEMA_NS =
+            "http://www.w3.org/2001/XMLSchema-instance";
+
+    private static final String OUTPATH = "D:\\TEMP\\zxzx\\";
+
+    private static final char BS_CHAR = (char) 0x005c; // Backslash
+    private static final String INDENT_UNIT = "\u0020\u0020";
+
+    private static final TimeZone TZ_TOKYO =
+            TimeZone.getTimeZone("Asia/Tokyo");
+
+
+    /**
+     * 隠れコンストラクタ。
+     */
+    private XmlUtils(){
+        throw new Error();
+    }
+
+
+    /**
+     * DOCTYPE宣言を出力する。
+     * @param writer 出力先
+     * @throws IOException 出力エラー
+     */
+    public static void dumpDocType(Writer writer) throws IOException{
+        writer.append("<!DOCTYPE village SYSTEM ");
+        writer.append('"');
+        writer.append(ORIG_DTD);
+        writer.append('"');
+        writer.append(" >");
+        return;
+    }
+
+    /**
+     * オリジナルNameSpace宣言を出力する。
+     * @param writer 出力先
+     * @throws IOException 出力エラー
+     */
+    public static void dumpNameSpaceDecl(Writer writer)
+            throws IOException{
+        attrOut(writer, "xmlns", ORIG_NS);
+        return;
+    }
+
+    /**
+     * スキーマNameSpace宣言を出力する。
+     * @param writer 出力先
+     * @throws IOException 出力エラー
+     */
+    public static void dumpSiNameSpaceDecl(Writer writer)
+            throws IOException{
+        attrOut(writer, "xmlns:xsi", SCHEMA_NS);
+        return;
+    }
+
+    /**
+     * スキーマ位置指定を出力する。
+     * @param writer 出力先
+     * @throws IOException 出力エラー
+     */
+    public static void dumpSchemeLocation(Writer writer)
+            throws IOException{
+        attrOut(writer,
+                "xsi:schemaLocation",
+                ORIG_NS + " " + ORIG_SCHEME);
+        return;
+    }
+
+    /**
+     * インデント用空白を出力する。
+     * ネスト単位は空白2文字
+     * @param writer 出力先
+     * @param level ネストレベル
+     * @throws IOException 出力エラー
+     */
+    public static void indent(Writer writer, int level) throws IOException{
+        for(int ct = 1; ct <= level; ct++){
+            writer.append(INDENT_UNIT);
+        }
+        return;
+    }
+
+    /**
+     * XML数値文字参照を出力する。
+     * @param writer 出力先
+     * @param chVal 出力文字
+     * @throws IOException 出力エラー
+     */
+    public static void charRefOut(Writer writer, char chVal)
+            throws IOException{
+        if(chVal == '\u0020'){
+            writer.append("&#x20;");
+            return;
+        }
+
+        if(chVal == '\u0009'){
+            writer.append("&#x09;");
+            return;
+        }
+
+        int ival = 0xffff & ((int) chVal);
+        String hex = Integer.toHexString(ival);
+        if(hex.length() % 2 != 0) hex = "0" + hex;
+
+        writer.append("&#x");
+        writer.append(hex);
+        writer.append(";");
+
+        return;
+    }
+
+    /**
+     * 不正文字をXML出力する。
+     * @param writer 出力先
+     * @param chVal 不正文字
+     * @throws IOException 出力エラー
+     */
+    public static void dumpInvalidChar(Writer writer, char chVal)
+            throws IOException{
+        int hexVal;
+        hexVal = chVal & 0xff;
+        String hexBin = Integer.toHexString(hexVal);
+        if(hexBin.length() % 2 != 0) hexBin = "0" + hexBin;
+
+        char replaceChar = '\ufffd';
+        if('\u0000' <= chVal && chVal <= '\u001f'){
+            replaceChar = (char)( chVal + '\u2400' );
+        }
+
+        writer.append("<rawdata");
+
+        writer.append(' ');
+        attrOut(writer, "encoding", "Shift_JIS");
+
+        writer.append(' ');
+        attrOut(writer, "hexBin", hexBin);
+
+        writer.append(" >");
+        writer.append(replaceChar);
+        writer.append("</rawdata>");
+    }
+
+    /**
+     * 任意の文字がXML規格上のホワイトスペースに属するか判定する。
+     * @param chVal 文字
+     * @return ホワイトスペースならtrue
+     */
+    public static boolean isWhiteSpace(char chVal){
+        switch(chVal){
+        case '\u0020':
+        case '\t':
+        case '\n':
+        case '\r':
+            return true;
+        default:
+            break;
+        }
+
+        return false;
+    }
+
+    /**
+     * 文字列を出力する。
+     * <ul>
+     * <li>先頭および末尾のホワイトスペースは強制的に文字参照化される。
+     * <li>連続したホワイトスペースの2文字目以降は文字参照化される。
+     * <li>スペースでないホワイトスペースは無条件に文字参照化される。
+     * <li>{@literal &, <, >, "}は無条件に文字参照化される。
+     * </ul>
+     * 参考:XML 1.0 規格 3.3.3節
+     * @param writer 出力先
+     * @param seq CDATA文字列
+     * @throws IOException 出力エラー
+     */
+    public static void textOut(Writer writer, CharSequence seq)
+            throws IOException{
+        int len = seq.length();
+
+        boolean leadSpace = false;
+
+        for(int pos = 0; pos < len; pos++){
+            char chVal = seq.charAt(pos);
+
+            if(isWhiteSpace(chVal)){
+                if(pos == 0 || pos >= len - 1 || leadSpace){
+                    charRefOut(writer, chVal);
+                }else if(chVal != '\u0020'){
+                    charRefOut(writer, chVal);
+                }else{
+                    writer.append(chVal);
+                }
+                leadSpace = true;
+            }else{
+                if(chVal == '&'){
+                    writer.append("&amp;");
+                }else if(chVal == '<'){
+                    writer.append("&lt;");
+                }else if(chVal == '>'){
+                    writer.append("&gt;");
+                }else if(chVal == '"'){
+                    writer.append("&quot;");
+                }else if(chVal == '\''){
+                    writer.append("&apos;");
+                }else if(chVal == BS_CHAR){
+                    writer.append('\u00a5');
+                }else if(chVal == '\u007e'){
+                    writer.append('\u203e');
+                }else if(Character.isISOControl(chVal)){
+                    dumpInvalidChar(writer, chVal);
+                }else{
+                    writer.append(chVal);
+                }
+                leadSpace = false;
+            }
+        }
+
+        return;
+    }
+
+    /**
+     * 属性を出力する。
+     * @param writer 出力先
+     * @param name 属性名
+     * @param value 属性値
+     * @throws IOException 出力エラー
+     */
+    public static void attrOut(Writer writer,
+                                CharSequence name,
+                                CharSequence value)
+            throws IOException{
+        StringBuilder newValue = new StringBuilder(value);
+        for(int pt = 0; pt < newValue.length(); pt++){
+            char chVal = newValue.charAt(pt);
+            if(chVal == '\n' || chVal == '\r' || chVal == '\t') continue;
+            if(Character.isISOControl(chVal)){
+                newValue.setCharAt(pt, (char)('\u2400' + chVal));
+            }
+        }
+
+        writer.append(name);
+        writer.append('=');
+        writer.append('"');
+        textOut(writer, newValue);
+        writer.append('"');
+        return;
+    }
+
+    /**
+     * xsd:time形式の時刻属性を出力する。
+     * タイムゾーンは「+09:00」固定
+     * @param writer 出力先
+     * @param name 属性名
+     * @param hour 時間
+     * @param minute 分
+     * @throws IOException 出力エラー
+     */
+    public static void timeAttrOut(Writer writer,
+                                     CharSequence name,
+                                     int hour, int minute)
+            throws IOException{
+        String cmtTime =
+                MessageFormat
+                .format("{0,number,#00}:{1,number,#00}:00+09:00",
+                        hour, minute);
+        attrOut(writer, name, cmtTime);
+        return;
+    }
+
+    /**
+     * xsd:gMonthDay形式の日付属性を出力する。
+     * タイムゾーンは「+09:00」固定
+     * @param writer 出力先
+     * @param name 属性名
+     * @param month 月
+     * @param day 日
+     * @throws IOException 出力エラー
+     */
+    public static void dateAttrOut(Writer writer,
+                                     CharSequence name,
+                                     int month, int day)
+            throws IOException{
+        String dateAttr =
+                MessageFormat.format("--{0,number,#00}-{1,number,#00}+09:00",
+                                     month, day);
+        attrOut(writer, name, dateAttr);
+        return;
+    }
+
+    /**
+     * xsd:dateTime形式の日付時刻属性を出力する。
+     * タイムゾーンは「+09:00」固定
+     * @param writer 出力先
+     * @param name 属性名
+     * @param epochMs エポック時刻
+     * @throws IOException 出力エラー
+     */
+    public static void dateTimeAttr(Writer writer,
+                                      CharSequence name,
+                                      long epochMs)
+            throws IOException{
+        Calendar calendar = new GregorianCalendar(TZ_TOKYO);
+
+        calendar.setTimeInMillis(epochMs);
+        int year = calendar.get(Calendar.YEAR);
+        int month = calendar.get(Calendar.MONTH) + 1;
+        int day = calendar.get(Calendar.DATE);
+        int hour = calendar.get(Calendar.HOUR_OF_DAY);
+        int minute = calendar.get(Calendar.MINUTE);
+        int sec = calendar.get(Calendar.SECOND);
+        int msec = calendar.get(Calendar.MILLISECOND);
+
+        String attrVal = MessageFormat.format(
+                 "{0,number,#0000}-{1,number,#00}-{2,number,#00}"
+                +"T{3,number,#00}:{4,number,#00}:{5,number,#00}"
+                +".{6,number,#000}+09:00",
+                year, month, day, hour, minute, sec, msec);
+
+        attrOut(writer, name, attrVal);
+
+        return;
+    }
+
+    /**
+     * デコードエラー情報をrawdataタグで出力する。
+     * 文字列集合に関するエラーの場合、windows31jでのデコード出力を試みる。
+     * @param writer 出力先
+     * @param errorInfo デコードエラー
+     * @throws IOException 出力エラー
+     */
+    public static void dumpErrorInfo(Writer writer,
+                                       DecodeErrorInfo errorInfo)
+            throws IOException{
+        int hexVal;
+        hexVal = errorInfo.getRawByte1st() & 0xff;
+        if(errorInfo.has2nd()){
+            hexVal <<= 8;
+            hexVal |= errorInfo.getRawByte2nd() & 0xff;
+        }
+
+        String hexBin = Integer.toHexString(hexVal);
+        if(hexBin.length() % 2 != 0) hexBin = "0" + hexBin;
+
+        char replaceChar = Win31j.getWin31jChar(errorInfo);
+
+        writer.append("<rawdata");
+
+        writer.append(' ');
+        attrOut(writer, "encoding", "Shift_JIS");
+
+        writer.append(' ');
+        attrOut(writer, "hexBin", hexBin);
+
+        writer.append(" >");
+        writer.append(replaceChar);
+        writer.append("</rawdata>");
+
+        return;
+    }
+
+    /**
+     * デコードエラー込みのテキストを出力する。
+     * @param writer 出力先
+     * @param content テキスト
+     * @throws IOException 出力エラー
+     */
+    public static void dumpDecodedContent(Writer writer,
+                                             DecodedContent content)
+            throws IOException{
+        if( ! content.hasDecodeError() ){
+            textOut(writer, content);
+            return;
+        }
+
+        int last = 0;
+
+        List<DecodeErrorInfo> errList = content.getDecodeErrorList();
+        for(DecodeErrorInfo err : errList){
+            int charPos = err.getCharPosition();
+            CharSequence line = content.subSequence(last, charPos);
+            textOut(writer, line);
+            dumpErrorInfo(writer, err);
+            last = charPos + 1;
+        }
+
+        CharSequence line = content.subSequence(last, content.length());
+        textOut(writer, line);
+
+        return;
+    }
+
+    /**
+     * 村情報をXML形式で出力する。
+     * @param writer 出力先
+     * @param villageData 村情報
+     * @throws IOException 出力エラー
+     */
+    public static void dumpVillageData(Writer writer,
+                                         VillageData villageData)
+            throws IOException{
+        writer.append("<?xml");
+        writer.append(' ');
+        attrOut(writer, "version", "1.0");
+        writer.append(' ');
+        attrOut(writer, "encoding", "UTF-8");
+        writer.append(" ?>\n\n");
+
+        writer.append("<!--\n");
+        writer.append("  人狼BBSアーカイブ\n");
+        writer.append("  http://jindolf.sourceforge.jp/\n");
+        writer.append("-->\n\n");
+
+        dumpDocType(writer);
+        writer.append("\n\n");
+
+        villageData.dumpXml(writer);
+
+        writer.append("\n<!-- EOF -->\n");
+
+        writer.flush();
+
+        return;
+    }
+
+    /**
+     * 村情報を反映した出力ファイル名を生成する。
+     * @param village 村情報
+     * @return XML出力ファイル名
+     */
+    public static String createOutFileName(VillageData village){
+        LandDef landDef = village.getLandDef();
+        String landId = landDef.getLandId();
+        int vid = village.getVillageId();
+
+        String fname =
+                MessageFormat.format(
+                "{0}jin_{1}_{2,number,#00000}.xml", OUTPATH, landId, vid);
+        return fname;
+    }
+
+    /**
+     * 村情報を反映した出力ファイルへの文字ストリームを生成する。
+     * @param village 村情報
+     * @return 出力先文字ストリーム
+     * @throws IOException 出力エラー
+     */
+    public static Writer createFileWriter(VillageData village)
+            throws IOException{
+        String fname = createOutFileName(village);
+        File file = new File(fname);
+
+        OutputStream ostream;
+        ostream = new FileOutputStream(file);
+        ostream = new BufferedOutputStream(ostream, 10000);
+        Writer writer;
+        writer = new OutputStreamWriter(ostream, "UTF-8");
+        writer = new BufferedWriter(writer, 10000);
+        return writer;
+    }
+
+}
index 0a587bb..769bdda 100644 (file)
-/*\r
- * ZIP utils\r
- *\r
- * License : The MIT License\r
- * Copyright(c) 2008 olyutorskii\r
- */\r
-\r
-package jp.sourceforge.jindolf.archiver;\r
-\r
-import java.io.File;\r
-import java.io.IOException;\r
-import java.io.InputStream;\r
-import java.io.Writer;\r
-import java.net.MalformedURLException;\r
-import java.net.URI;\r
-import java.net.URISyntaxException;\r
-import java.net.URL;\r
-import java.util.Enumeration;\r
-import java.util.Iterator;\r
-import java.util.LinkedList;\r
-import java.util.List;\r
-import java.util.regex.Matcher;\r
-import java.util.regex.Pattern;\r
-import java.util.zip.ZipEntry;\r
-import java.util.zip.ZipFile;\r
-import jp.sourceforge.jindolf.parser.DecodeException;\r
-import jp.sourceforge.jindolf.parser.HtmlParseException;\r
-\r
-/**\r
- * ZIPアーカイブされた生XHTML情報へのアクセス諸々。\r
- */\r
-public final class ZipUtils{\r
-\r
-    /**\r
-     * 隠れコンストラクタ。\r
-     */\r
-    private ZipUtils(){\r
-        super();\r
-        return;\r
-    }\r
-\r
-\r
-    /**\r
-     * ZIPファイルに格納された村一覧を抽出する。\r
-     * 各日のロードはまだ行われない。\r
-     * @param zipFile ZIPファイル\r
-     * @return 村一覧\r
-     * @throws IOException 入力エラー\r
-     */\r
-    public static List<VillageData> getVillageDataList(ZipFile zipFile)\r
-            throws IOException{\r
-        List<VillageData> result = new LinkedList<VillageData>();\r
-\r
-        List<ZipEntry> logList = getDownloadLogList(zipFile);\r
-        for(ZipEntry entry : logList){\r
-            VillageData villageData =\r
-                getVillageDataFromLogEntry(zipFile, entry);\r
-            result.add(villageData);\r
-        }\r
-\r
-        return result;\r
-    }\r
-\r
-    /**\r
-     * ZIPファイルからダウンロードログファイルの一覧を取得する。\r
-     * ダウンロードログファイルは必ず「download.log」の名前を持つ。\r
-     * @param file ZIPファイル\r
-     * @return ログファイル一覧\r
-     */\r
-    public static List<ZipEntry> getDownloadLogList(ZipFile file){\r
-        List<ZipEntry> result = new LinkedList<ZipEntry>();\r
-\r
-        Enumeration<? extends ZipEntry> list = file.entries();\r
-        while(list.hasMoreElements()){\r
-            ZipEntry entry = list.nextElement();\r
-            String name = entry.getName();\r
-            if(name.endsWith("/download.log" )){\r
-                result.add(entry);\r
-            }\r
-        }\r
-\r
-        return result;\r
-    }\r
-\r
-    /**\r
-     * ログファイルを表すZIPエントリから村情報を抽出する。\r
-     * @param zipFile ZIPファイル\r
-     * @param logEntry ログファイルのZIPエントリ\r
-     * @return 村情報\r
-     * @throws IOException 入力エラー\r
-     */\r
-    public static VillageData getVillageDataFromLogEntry(\r
-            ZipFile zipFile, ZipEntry logEntry)\r
-            throws IOException{\r
-        InputStream istream = zipFile.getInputStream(logEntry);\r
-        List<PeriodResource> list =\r
-                FileArchive.parseDownloadLog(istream);\r
-        istream.close();\r
-\r
-        String baseName = logEntry.getName().replaceAll("/[^/]+$", "/");\r
-        for(PeriodResource resource : list){\r
-            modifyResourceUrl(zipFile, baseName, resource);\r
-        }\r
-\r
-        VillageData villageData = new VillageData(list);\r
-        return villageData;\r
-    }\r
-\r
-    /**\r
-     * ログ記録に書かれたXHTMLファイル名を実際にアクセス可能なURLに修正する。\r
-     * @param zipFile ZIPファイル\r
-     * @param entryBase ファイル名のベース\r
-     * @param resource リソース情報\r
-     * @return 引数と同じ物\r
-     */\r
-    public static PeriodResource modifyResourceUrl(ZipFile zipFile,\r
-                                           String entryBase,\r
-                                           PeriodResource resource ){\r
-        String fileName;\r
-        try{\r
-            URL resUrl = resource.getResourceUrl();\r
-            URI resUri = resUrl.toURI();\r
-            File file = new File(resUri);\r
-            fileName = file.getName();\r
-        }catch(URISyntaxException e){\r
-            throw new IllegalArgumentException(e);\r
-        }\r
-\r
-        String zipUri = new File(zipFile.getName()).toURI().toString();\r
-\r
-        String urlText = "jar:" + zipUri + "!/" + entryBase + fileName;\r
-\r
-        URL zipResource;\r
-        try{\r
-            zipResource = new URL(urlText);\r
-        }catch(MalformedURLException e){\r
-            throw new IllegalArgumentException(e);\r
-        }\r
-\r
-        resource.setResourceUrl(zipResource);\r
-\r
-        return resource;\r
-    }\r
-\r
-    /**\r
-     * 村番号から村情報を得る。\r
-     * @param zipFile ZIPファイル\r
-     * @param vid 村番号\r
-     * @return 村情報\r
-     * @throws IOException 入力エラー\r
-     */\r
-    public static VillageData getVillageData(ZipFile zipFile, int vid)\r
-            throws IOException{\r
-        ZipEntry entry = getDownloadLogEntry(zipFile, vid);\r
-        VillageData result = getVillageDataFromLogEntry(zipFile, entry);\r
-        return result;\r
-    }\r
-\r
-    /**\r
-     * 村番号から該当するログファイルエントリを取得する。\r
-     * @param file ZIPファイル\r
-     * @param vid 村番号\r
-     * @return ログファイルのZIPエントリ。見つからなければnull。\r
-     */\r
-    public static ZipEntry getDownloadLogEntry(ZipFile file, int vid){\r
-        Pattern entryPattern =\r
-                Pattern.compile("jin_[^_]+_([0-9]+)/download.log$");\r
-\r
-        Enumeration<? extends ZipEntry> list = file.entries();\r
-        while(list.hasMoreElements()){\r
-            ZipEntry entry = list.nextElement();\r
-            String name = entry.getName();\r
-            Matcher matcher = entryPattern.matcher(name);\r
-            if(matcher.find()){\r
-                String vnum = matcher.group(1);\r
-                if(vid == Integer.parseInt(vnum)) return entry;\r
-            }\r
-        }\r
-        return null;\r
-    }\r
-\r
-    /**\r
-     * ZIPファイル中の指定した村番号の村をXML出力する。\r
-     * @param zipFile ZIPファイル\r
-     * @param vid 村番号\r
-     * @param writer 出力先\r
-     * @throws IOException 入出力エラー\r
-     * @throws DecodeException デコードエラー\r
-     * @throws HtmlParseException パースエラー\r
-     */\r
-    public static void dumpZipVid(ZipFile zipFile, int vid, Writer writer)\r
-            throws IOException, DecodeException, HtmlParseException{\r
-        VillageData data = getVillageData(zipFile, vid);\r
-        Builder.fillVillageData(data);\r
-        XmlUtils.dumpVillageData(writer, data);\r
-\r
-        return;\r
-    }\r
-\r
-    /**\r
-     * ZIPファイル中の全村をXML出力する。\r
-     * @param zipFile ZIPファイル\r
-     * @throws IOException 入出力エラー\r
-     * @throws DecodeException デコードエラー\r
-     * @throws HtmlParseException パースエラー\r
-     */\r
-    public static void dumpZipAll(ZipFile zipFile)\r
-            throws IOException, DecodeException, HtmlParseException{\r
-        List<VillageData> villageDataList;\r
-        villageDataList = ZipUtils.getVillageDataList(zipFile);\r
-        Iterator<VillageData> it = villageDataList.iterator();\r
-        while(it.hasNext()){\r
-            VillageData villageData = it.next();\r
-            Builder.fillVillageData(villageData);\r
-            Writer writer = XmlUtils.createFileWriter(villageData);\r
-            XmlUtils.dumpVillageData(writer, villageData);\r
-            writer.close();\r
-            it.remove();\r
-        }\r
-\r
-        return;\r
-    }\r
-\r
-}\r
+/*
+ * ZIP utils
+ *
+ * License : The MIT License
+ * Copyright(c) 2008 olyutorskii
+ */
+
+package jp.sourceforge.jindolf.archiver;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Writer;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import jp.sourceforge.jindolf.parser.DecodeException;
+import jp.sourceforge.jindolf.parser.HtmlParseException;
+
+/**
+ * ZIPアーカイブされた生XHTML情報へのアクセス諸々。
+ */
+public final class ZipUtils{
+
+    /**
+     * 隠れコンストラクタ。
+     */
+    private ZipUtils(){
+        super();
+        return;
+    }
+
+
+    /**
+     * ZIPファイルに格納された村一覧を抽出する。
+     * 各日のロードはまだ行われない。
+     * @param zipFile ZIPファイル
+     * @return 村一覧
+     * @throws IOException 入力エラー
+     */
+    public static List<VillageData> getVillageDataList(ZipFile zipFile)
+            throws IOException{
+        List<VillageData> result = new LinkedList<VillageData>();
+
+        List<ZipEntry> logList = getDownloadLogList(zipFile);
+        for(ZipEntry entry : logList){
+            VillageData villageData =
+                getVillageDataFromLogEntry(zipFile, entry);
+            result.add(villageData);
+        }
+
+        return result;
+    }
+
+    /**
+     * ZIPファイルからダウンロードログファイルの一覧を取得する。
+     * ダウンロードログファイルは必ず「download.log」の名前を持つ。
+     * @param file ZIPファイル
+     * @return ログファイル一覧
+     */
+    public static List<ZipEntry> getDownloadLogList(ZipFile file){
+        List<ZipEntry> result = new LinkedList<ZipEntry>();
+
+        Enumeration<? extends ZipEntry> list = file.entries();
+        while(list.hasMoreElements()){
+            ZipEntry entry = list.nextElement();
+            String name = entry.getName();
+            if(name.endsWith("/download.log" )){
+                result.add(entry);
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * ログファイルを表すZIPエントリから村情報を抽出する。
+     * @param zipFile ZIPファイル
+     * @param logEntry ログファイルのZIPエントリ
+     * @return 村情報
+     * @throws IOException 入力エラー
+     */
+    public static VillageData getVillageDataFromLogEntry(
+            ZipFile zipFile, ZipEntry logEntry)
+            throws IOException{
+        InputStream istream = zipFile.getInputStream(logEntry);
+        List<PeriodResource> list =
+                FileArchive.parseDownloadLog(istream);
+        istream.close();
+
+        String baseName = logEntry.getName().replaceAll("/[^/]+$", "/");
+        for(PeriodResource resource : list){
+            modifyResourceUrl(zipFile, baseName, resource);
+        }
+
+        VillageData villageData = new VillageData(list);
+        return villageData;
+    }
+
+    /**
+     * ログ記録に書かれたXHTMLファイル名を実際にアクセス可能なURLに修正する。
+     * @param zipFile ZIPファイル
+     * @param entryBase ファイル名のベース
+     * @param resource リソース情報
+     * @return 引数と同じ物
+     */
+    public static PeriodResource modifyResourceUrl(ZipFile zipFile,
+                                           String entryBase,
+                                           PeriodResource resource ){
+        String fileName;
+        try{
+            URL resUrl = resource.getResourceUrl();
+            URI resUri = resUrl.toURI();
+            File file = new File(resUri);
+            fileName = file.getName();
+        }catch(URISyntaxException e){
+            throw new IllegalArgumentException(e);
+        }
+
+        String zipUri = new File(zipFile.getName()).toURI().toString();
+
+        String urlText = "jar:" + zipUri + "!/" + entryBase + fileName;
+
+        URL zipResource;
+        try{
+            zipResource = new URL(urlText);
+        }catch(MalformedURLException e){
+            throw new IllegalArgumentException(e);
+        }
+
+        resource.setResourceUrl(zipResource);
+
+        return resource;
+    }
+
+    /**
+     * 村番号から村情報を得る。
+     * @param zipFile ZIPファイル
+     * @param vid 村番号
+     * @return 村情報
+     * @throws IOException 入力エラー
+     */
+    public static VillageData getVillageData(ZipFile zipFile, int vid)
+            throws IOException{
+        ZipEntry entry = getDownloadLogEntry(zipFile, vid);
+        VillageData result = getVillageDataFromLogEntry(zipFile, entry);
+        return result;
+    }
+
+    /**
+     * 村番号から該当するログファイルエントリを取得する。
+     * @param file ZIPファイル
+     * @param vid 村番号
+     * @return ログファイルのZIPエントリ。見つからなければnull。
+     */
+    public static ZipEntry getDownloadLogEntry(ZipFile file, int vid){
+        Pattern entryPattern =
+                Pattern.compile("jin_[^_]+_([0-9]+)/download.log$");
+
+        Enumeration<? extends ZipEntry> list = file.entries();
+        while(list.hasMoreElements()){
+            ZipEntry entry = list.nextElement();
+            String name = entry.getName();
+            Matcher matcher = entryPattern.matcher(name);
+            if(matcher.find()){
+                String vnum = matcher.group(1);
+                if(vid == Integer.parseInt(vnum)) return entry;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * ZIPファイル中の指定した村番号の村をXML出力する。
+     * @param zipFile ZIPファイル
+     * @param vid 村番号
+     * @param writer 出力先
+     * @throws IOException 入出力エラー
+     * @throws DecodeException デコードエラー
+     * @throws HtmlParseException パースエラー
+     */
+    public static void dumpZipVid(ZipFile zipFile, int vid, Writer writer)
+            throws IOException, DecodeException, HtmlParseException{
+        VillageData data = getVillageData(zipFile, vid);
+        Builder.fillVillageData(data);
+        XmlUtils.dumpVillageData(writer, data);
+
+        return;
+    }
+
+    /**
+     * ZIPファイル中の全村をXML出力する。
+     * @param zipFile ZIPファイル
+     * @throws IOException 入出力エラー
+     * @throws DecodeException デコードエラー
+     * @throws HtmlParseException パースエラー
+     */
+    public static void dumpZipAll(ZipFile zipFile)
+            throws IOException, DecodeException, HtmlParseException{
+        List<VillageData> villageDataList;
+        villageDataList = ZipUtils.getVillageDataList(zipFile);
+        Iterator<VillageData> it = villageDataList.iterator();
+        while(it.hasNext()){
+            VillageData villageData = it.next();
+            Builder.fillVillageData(villageData);
+            Writer writer = XmlUtils.createFileWriter(villageData);
+            XmlUtils.dumpVillageData(writer, villageData);
+            writer.close();
+            it.remove();
+        }
+
+        return;
+    }
+
+}
index 7ccac8f..5caa9c4 100644 (file)
@@ -1,56 +1,56 @@
-/*\r
- * JinArchive パッケージコメント\r
- *\r
- * このファイルは、SunJDK5.0以降に含まれるJavadoc用に用意された、\r
- * 特別な名前を持つソースファイルです。\r
- * このファイルはソースコードを含まず、\r
- * パッケージコメントとパッケージ宣言のみが含まれます。\r
- *\r
- * License : The MIT License\r
- * Copyright(c) 2008 olyutorskii\r
- */\r
-\r
-/**\r
- * このプログラム「JinArchive」は、\r
- * 人狼BBSサーバへアクセスし任意の村のプレイデータを独自のXML形式で\r
- * 保存するためのプログラムです。\r
- *\r
- * <hr>\r
- *\r
- * <p>\r
- * The MIT License\r
- * </p>\r
- * <p>\r
- * Copyright(c) 2009 olyutorskii\r
- * </p>\r
- * <p>\r
- * Permission is hereby granted, free of charge, to any person obtaining a\r
- * copy of this software and associated documentation files (the "Software"),\r
- * to deal in the Software without restriction, including without limitation\r
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,\r
- * and/or sell copies of the Software, and to permit persons to whom the\r
- * Software is furnished to do so, subject to the following conditions:\r
- * </p>\r
- * <p>\r
- * The above copyright notice and this permission notice shall be included in\r
- * all copies or substantial portions of the Software.\r
- * </p>\r
- * <p>\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\r
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\r
- * OTHER DEALINGS IN THE SOFTWARE.\r
- * </p>\r
- *\r
- * <hr>\r
- *\r
- * @see <a href="http://jindolf.sourceforge.jp/">\r
- * Jindolfポータルサイト</a>\r
- * @see <a href="http://sourceforge.jp/projects/jindolf/">\r
- * Jindolf開発プロジェクト</a>\r
- */\r
-\r
-package jp.sourceforge.jindolf.archiver;\r
+/*
+ * JinArchive パッケージコメント
+ *
+ * このファイルは、SunJDK5.0以降に含まれるJavadoc用に用意された、
+ * 特別な名前を持つソースファイルです。
+ * このファイルはソースコードを含まず、
+ * パッケージコメントとパッケージ宣言のみが含まれます。
+ *
+ * License : The MIT License
+ * Copyright(c) 2008 olyutorskii
+ */
+
+/**
+ * このプログラム「JinArchive」は、
+ * 人狼BBSサーバへアクセスし任意の村のプレイデータを独自のXML形式で
+ * 保存するためのプログラムです。
+ *
+ * <hr>
+ *
+ * <p>
+ * The MIT License
+ * </p>
+ * <p>
+ * Copyright(c) 2009 olyutorskii
+ * </p>
+ * <p>
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * </p>
+ * <p>
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * </p>
+ * <p>
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ * </p>
+ *
+ * <hr>
+ *
+ * @see <a href="http://jindolf.sourceforge.jp/">
+ * Jindolfポータルサイト</a>
+ * @see <a href="http://sourceforge.jp/projects/jindolf/">
+ * Jindolf開発プロジェクト</a>
+ */
+
+package jp.sourceforge.jindolf.archiver;