OSDN Git Service

XML出力改善
authorOlyutorskii <olyutorskii@users.osdn.me>
Mon, 31 Dec 2012 16:50:40 +0000 (01:50 +0900)
committerOlyutorskii <olyutorskii@users.osdn.me>
Mon, 31 Dec 2012 16:50:40 +0000 (01:50 +0900)
ジェネレータ非表示オプション追加
コンバータ分離

17 files changed:
.hgeol
CHANGELOG.txt
src/main/java/jp/sourceforge/mikutoga/pmd/model/xml/PmdXmlExporter.java
src/main/java/jp/sourceforge/mikutoga/pmd2xml/ModelFileTypes.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd2xml/OptInfo.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd2xml/Pmd2Xml.java
src/main/java/jp/sourceforge/mikutoga/pmd2xml/Pmd2XmlConv.java [new file with mode: 0644]
src/test/java/testdata/CnvAssert.java [new file with mode: 0644]
src/test/java/testdata/charset/CharsetTest.java [new file with mode: 0644]
src/test/java/testdata/minimum/MinimumTest.java [new file with mode: 0644]
src/test/resources/testdata/charset/Readme.txt [new file with mode: 0644]
src/test/resources/testdata/charset/result.xml [new file with mode: 0644]
src/test/resources/testdata/charset/source.xml [new file with mode: 0644]
src/test/resources/testdata/charset/test.pmd [new file with mode: 0644]
src/test/resources/testdata/minimum/Readme.txt [new file with mode: 0644]
src/test/resources/testdata/minimum/test.pmd [new file with mode: 0644]
src/test/resources/testdata/minimum/test.xml [new file with mode: 0644]

diff --git a/.hgeol b/.hgeol
index 8458257..71b275b 100644 (file)
--- a/.hgeol
+++ b/.hgeol
@@ -17,3 +17,5 @@
 **.gif = BIN
 **.jpeg = BIN
 **.jpg = BIN
 **.gif = BIN
 **.jpeg = BIN
 **.jpg = BIN
+**.pmd = BIN
+
index 8c4d115..539010a 100644 (file)
@@ -6,6 +6,7 @@ Pmd2XML 変更履歴
 
 X.XXX.X (20XX-XX-XX)
     ・Maven3対応。
 
 X.XXX.X (20XX-XX-XX)
     ・Maven3対応。
+    ・XML出力改行コードの指定が可能になる。
 
 1.102.4 (2010-10-09)
     ・TogaGem1.105.2のバグ修正(接続ボーンを持たない剛体の件)に対処。
 
 1.102.4 (2010-10-09)
     ・TogaGem1.105.2のバグ修正(接続ボーンを持たない剛体の件)に対処。
index 705a67c..ff9856d 100644 (file)
@@ -92,7 +92,7 @@ public class PmdXmlExporter extends BasicXmlExporter{
         + "[1 : ONLYDYNAMICS  : 物理演算         ]\n"
         + "[2 : BONEDDYNAMICS : ボーン位置合わせ ]\n";
 
         + "[1 : ONLYDYNAMICS  : 物理演算         ]\n"
         + "[2 : BONEDDYNAMICS : ボーン位置合わせ ]\n";
 
-    private String generator = "";
+    private String generator = null;
 
     /**
      * コンストラクタ。
 
     /**
      * コンストラクタ。
@@ -106,12 +106,9 @@ public class PmdXmlExporter extends BasicXmlExporter{
 
     /**
      * Generatorメタ情報を設定する。
 
     /**
      * Generatorメタ情報を設定する。
-     * @param generatorArg Generatorメタ情報
-     * @throws NullPointerException 引数がnull
+     * @param generatorArg Generatorメタ情報。表示したくないときはnull
      */
      */
-    public void setGenerator(String generatorArg)
-            throws NullPointerException{
-        if(generatorArg == null) throw new NullPointerException();
+    public void setGenerator(String generatorArg){
         this.generator = generatorArg;
         return;
     }
         this.generator = generatorArg;
         return;
     }
@@ -366,7 +363,6 @@ public class PmdXmlExporter extends BasicXmlExporter{
 
         putBRedContent(text);
 
 
         putBRedContent(text);
 
-        ln();
         ind().put("</description>").ln();
 
         if( ! hasOnlyBasicLatin(text) && isBasicLatinOnlyOut() ){
         ind().put("</description>").ln();
 
         if( ! hasOnlyBasicLatin(text) && isBasicLatinOnlyOut() ){
@@ -389,26 +385,22 @@ public class PmdXmlExporter extends BasicXmlExporter{
             throws IOException{
         int length = content.length();
 
             throws IOException{
         int length = content.length();
 
-        for(int pos = 0; pos < length; pos++){
-            char ch = content.charAt(pos);
+        int startPos = 0;
+
+        for(int idx = 0; idx < length; idx++){
+            char ch = content.charAt(idx);
             if(ch == '\n'){
             if(ch == '\n'){
-                put("<br/>").ln();
-            }else if(Character.isISOControl(ch)){
-                putCharRef2Hex(ch);
-            }else if( ! isBasicLatin(ch) && isBasicLatinOnlyOut()){
-                putCharRef4Hex(ch);
-            }else{
-                switch(ch){
-                case '&':  put("&amp;");  break;
-                case '<':  put("&lt;");   break;
-                case '>':  put("&gt;");   break;
-                case '"':  put("&quot;"); break;
-                case '\'': put("&apos;"); break;
-                default:   put(ch);       break;
-                }
+                CharSequence seq = content.subSequence(startPos, idx);
+                putContent(seq).put("<br/>").ln();
+                startPos = idx + 1;
             }
         }
 
             }
         }
 
+        if(startPos < length){
+            CharSequence seq = content.subSequence(startPos, length);
+            putContent(seq).ln();
+        }
+
         return this;
     }
 
         return this;
     }
 
@@ -425,10 +417,13 @@ public class PmdXmlExporter extends BasicXmlExporter{
         ind().put("<credits>").ln();
         ind().put("</credits>").ln(2);
 
         ind().put("<credits>").ln();
         ind().put("</credits>").ln(2);
 
-        ind().put("<meta ");
-        putAttr("name", "generator").sp()
-                                    .putAttr("content", this.generator);
-        put(" />").ln();
+        if(this.generator != null){
+            ind().put("<meta ");
+            putAttr("name", "generator").sp()
+                                        .putAttr("content", this.generator);
+            put(" />").ln();
+        }
+
         ind().put("<meta ");
         putAttr("name", "siteURL").sp().putAttr("content", "");
         put(" />").ln();
         ind().put("<meta ");
         putAttr("name", "siteURL").sp().putAttr("content", "");
         put(" />").ln();
diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd2xml/ModelFileTypes.java b/src/main/java/jp/sourceforge/mikutoga/pmd2xml/ModelFileTypes.java
new file mode 100644 (file)
index 0000000..14d51f7
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * MMD model file types.
+ *
+ * License : The MIT License
+ * Copyright(c) 2012 MikuToga Partners
+ */
+
+package jp.sourceforge.mikutoga.pmd2xml;
+
+/**
+ * モデルファイル種別。
+ */
+public enum ModelFileTypes {
+    /** 不明。 */
+    NONE,
+
+    /** MikuMikuDance ver7 前後で読み書きが可能なPMDファイル。 */
+    PMD,
+
+    /**
+     * スキーマ
+     * http://mikutoga.sourceforge.jp/xml/xsd/pmdxml-101009.xsd
+     * で定義されたXMLファイル。
+     */
+    XML_101009,
+
+}
diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd2xml/OptInfo.java b/src/main/java/jp/sourceforge/mikutoga/pmd2xml/OptInfo.java
new file mode 100644 (file)
index 0000000..f50f2de
--- /dev/null
@@ -0,0 +1,328 @@
+/*
+ * command line argument info
+ *
+ * License : The MIT License
+ * Copyright(c) 2012 MikuToga Partners
+ */
+
+package jp.sourceforge.mikutoga.pmd2xml;
+
+import java.text.MessageFormat;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * コマンドラインオプション情報。
+ */
+final class OptInfo {
+
+    private static final String EOL_LF = "\n";
+    private static final String EOL_CRLF = "\r\n";
+    private static final String EOL_DEFAULT = EOL_LF;
+
+    private static final String GENERATOR =
+            Pmd2Xml.APPNAME + ' ' + Pmd2Xml.APPVER;
+
+    private static final String HELP_CONSOLE =
+              "-h       : put help message\n"
+            + "-pmd2xml : convert *.pmd to *.xml\n"
+            + "-xml2pmd : convert *.xml to *.pmd\n"
+            + "-i file  : specify input file\n"
+            + "-o file  : specify output file\n"
+            + "-f       : force overwriting\n"
+            + "-lf      : use LF as XML-newline (default)\n"
+            + "-crlf    : use CR+LF as XML-newline\n"
+            + "-gen     : print generator to XML (default)\n"
+            + "-nogen   : do not print generator to XML\n"
+            ;
+
+    private static final String ERRMSG_UNKNOWN =
+            "Unknown option : {0}";
+    private static final String ERRMSG_NODIR =
+            "You must specify -pmd2xml or -xml2pmd.";
+    private static final String ERRMSG_NOINFILE =
+            "You must specify input file with -i.";
+    private static final String ERRMSG_NOOUTFILE =
+            "You must specify output file with -o.";
+
+
+    private boolean hasError = false;
+    private String errMsg = null;
+
+    private boolean needHelp = false;
+    private ModelFileTypes inTypes  = ModelFileTypes.NONE;
+    private ModelFileTypes outTypes = ModelFileTypes.NONE;
+    private String inFilename = null;
+    private String outFilename = null;
+    private boolean overwrite = false;
+    private String newline = EOL_DEFAULT;
+    private String generator = GENERATOR;
+
+
+    /**
+     * コンストラクタ。
+     */
+    private OptInfo(){
+        super();
+        return;
+    }
+
+
+    /**
+     * コマンドラインを解析する。
+     * @param args コマンドライン
+     * @return オプション情報
+     */
+    static OptInfo parseOption(String... args){
+        OptInfo result = new OptInfo();
+
+        int argIdx = 0;
+        int argLength = args.length;
+
+        argline: while(argIdx < argLength){
+            String arg = args[argIdx];
+
+            OptSwitch opt = OptSwitch.find(arg);
+            if(opt == null){
+                String errMsg = MessageFormat.format(ERRMSG_UNKNOWN, arg);
+                result.putErrMsg(errMsg);
+                break argline;
+            }
+
+            switch(opt){
+            case OPT_HELP:
+                result.needHelp = true;
+                break argline;
+            case OPT_FORCE:
+                result.overwrite = true;
+                break;
+            case OPT_PMD2XML:
+                result.inTypes  = ModelFileTypes.PMD;
+                result.outTypes = ModelFileTypes.XML_101009;
+                break;
+            case OPT_XML2PMD:
+                result.inTypes  = ModelFileTypes.XML_101009;
+                result.outTypes = ModelFileTypes.PMD;
+                break;
+            case OPT_INFILE:
+                argIdx++;
+                if(argIdx >= argLength){
+                    result.putErrMsg(ERRMSG_NOINFILE);
+                    break argline;
+                }
+                result.inFilename = args[argIdx];
+                break;
+            case OPT_OUTFILE:
+                argIdx++;
+                if(argIdx >= argLength){
+                    result.putErrMsg(ERRMSG_NOOUTFILE);
+                    break argline;
+                }
+                result.outFilename = args[argIdx];
+                break;
+            case OPT_LF:
+                result.newline = EOL_LF;
+                break;
+            case OPT_CRLF:
+                result.newline = EOL_CRLF;
+                break;
+            case OPT_GEN:
+                result.generator = GENERATOR;
+                break;
+            case OPT_NOGEN:
+                result.generator = null;
+                break;
+            default:
+                assert false;
+                String errMsg = MessageFormat.format(ERRMSG_UNKNOWN, arg);
+                result.putErrMsg(errMsg);
+                break argline;
+            }
+
+            if(result.hasError()) return result;
+
+            argIdx++;
+        }
+
+        if(result.hasError()) return result;
+        if(result.needHelp()) return result;
+
+        checkResult(result);
+
+        return result;
+    }
+
+    /**
+     * オプション整合性の事後検査。
+     * @param result オプション情報
+     */
+    private static void checkResult(OptInfo result){
+        if(   result.getInFileType()  == ModelFileTypes.NONE
+           || result.getOutFileType() == ModelFileTypes.NONE ){
+            result.putErrMsg(ERRMSG_NODIR);
+            return;
+        }
+
+        if(result.getInFilename() == null){
+            result.putErrMsg(ERRMSG_NOINFILE);
+            return;
+        }
+
+        if(result.getOutFilename() == null){
+            result.putErrMsg(ERRMSG_NOOUTFILE);
+            return;
+        }
+
+        return;
+    }
+
+    /**
+     * コンソール提示用ヘルプ出力文字列を返す。
+     * @return オプションヘルプ文字列
+     */
+    static String getConsoleHelp(){
+        return HELP_CONSOLE;
+    }
+
+
+    /**
+     * 解析中にエラーが起きたか判定する。
+     * @return エラーが起きていればtrue
+     */
+    boolean hasError(){
+        return this.hasError;
+    }
+
+    /**
+     * エラーメッセージを返す。
+     * @return エラーメッセージ。なければnull
+     */
+    String getErrorMessage(){
+        return this.errMsg;
+    }
+
+    /**
+     * ヘルプ表示が必要か否か判定する。
+     * @return 必要ならtrue
+     */
+    boolean needHelp(){
+        return this.needHelp;
+    }
+
+    /**
+     * 入力ファイル種別を返す。
+     * @return 入力ファイル種別
+     */
+    ModelFileTypes getInFileType(){
+        return this.inTypes;
+    }
+
+    /**
+     * 出力ファイル種別を返す。
+     * @return 出力ファイル種別
+     */
+    ModelFileTypes getOutFileType(){
+        return this.outTypes;
+    }
+
+    /**
+     * 入力ファイル名を返す。
+     * @return 入力ファイル名
+     */
+    String getInFilename(){
+        return this.inFilename;
+    }
+
+    /**
+     * 出力ファイル名を返す。
+     * @return 出力ファイル名
+     */
+    String getOutFilename(){
+        return this.outFilename;
+    }
+
+    /**
+     * 上書きモードか否か返す。
+     * @return 上書きモードならtrue
+     */
+    boolean overwriteMode(){
+        return this.overwrite;
+    }
+
+    /**
+     * XML改行文字を返す。
+     * @return 改行文字
+     */
+    String getNewline(){
+        return this.newline;
+    }
+
+    /**
+     * ジェネレータ名を返す。
+     * @return ジェネレータ名。表示したくない時はnull
+     */
+    String getGenerator(){
+        return this.generator;
+    }
+
+    /**
+     * オプション解析エラー情報を設定する。
+     * @param txt エラー文字列
+     */
+    private void putErrMsg(String txt){
+        this.hasError = true;
+        this.errMsg = txt;
+        return;
+    }
+
+
+    /**
+     * オプションスイッチ群。
+     */
+    static enum OptSwitch{
+        OPT_HELP    ("-h", "-help", "-?"),
+        OPT_XML2PMD ("-xml2pmd"),
+        OPT_PMD2XML ("-pmd2xml"),
+        OPT_INFILE  ("-i"),
+        OPT_OUTFILE ("-o"),
+        OPT_FORCE   ("-f"),
+        OPT_LF      ("-lf"),
+        OPT_CRLF    ("-crlf"),
+        OPT_GEN     ("-gen"),
+        OPT_NOGEN   ("-nogen"),
+        ;
+
+
+        /**
+         * コンストラクタ。
+         * @param cmdargs オプションスイッチパターン群
+         */
+        private OptSwitch(String... cmdargs){
+            for(String cmdarg : cmdargs){
+                MapHolder.MAP_OPT.put(cmdarg, this);
+            }
+            return;
+        }
+
+        /**
+         * パターンに合致するオプションを見つける。
+         * @param cmd パターン
+         * @return オプション。見つからなければnull
+         */
+        static OptSwitch find(String cmd){
+            OptSwitch result = MapHolder.MAP_OPT.get(cmd);
+            return result;
+        }
+
+
+        /**
+         * enumコンストラクタからクラス変数にアクセスできない文法を回避。
+         */
+        private static class MapHolder{
+            static final Map<String, OptSwitch> MAP_OPT =
+                    new HashMap<String, OptSwitch>();
+        }
+
+    }
+
+}
index 0dd61ee..8e0b3c4 100644 (file)
@@ -11,24 +11,17 @@ import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.BufferedOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.io.PrintStream;
 import java.nio.channels.FileChannel;
 import java.util.Properties;
 import java.nio.channels.FileChannel;
 import java.util.Properties;
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.ParserConfigurationException;
 import jp.sourceforge.mikutoga.parser.MmdFormatException;
 import jp.sourceforge.mikutoga.pmd.IllegalPmdDataException;
 import jp.sourceforge.mikutoga.parser.MmdFormatException;
 import jp.sourceforge.mikutoga.pmd.IllegalPmdDataException;
-import jp.sourceforge.mikutoga.pmd.model.PmdModel;
-import jp.sourceforge.mikutoga.pmd.model.binio.PmdExporter;
-import jp.sourceforge.mikutoga.pmd.model.binio.PmdLoader;
-import jp.sourceforge.mikutoga.pmd.model.xml.PmdXmlExporter;
-import jp.sourceforge.mikutoga.pmd.model.xml.PmdXmlResources;
-import jp.sourceforge.mikutoga.pmd.model.xml.Xml2PmdLoader;
 import jp.sourceforge.mikutoga.xml.TogaXmlException;
 import jp.sourceforge.mikutoga.xml.TogaXmlException;
-import org.xml.sax.InputSource;
 import org.xml.sax.SAXException;
 
 /**
 import org.xml.sax.SAXException;
 
 /**
@@ -36,10 +29,31 @@ import org.xml.sax.SAXException;
  */
 public final class Pmd2Xml {
 
  */
 public final class Pmd2Xml {
 
+    /** 正常系。 */
+    public static final int EXIT_OK     = 0;
+    /** ファイル入出力に起因するエラー。 */
+    public static final int EXIT_FILE   = 1;
+    /** XMLフォーマットに起因するエラー。 */
+    public static final int EXIT_XML    = 2;
+    /** PMDフォーマットに起因するエラー。 */
+    public static final int EXIT_PMD    = 3;
+    /** 実行環境に起因するエラー。 */
+    public static final int EXIT_JREVER = 4;
+    /** オプション指定に起因するエラー。 */
+    public static final int EXIT_OPT    = 5;
+    /** 内部エラー。 */
+    public static final int EXIT_INTERN = 6;
+
+    /** アプリ名。 */
+    public static final String APPNAME;
+    /** バージョン識別子。 */
+    public static final String APPVER;
+    /** ライセンス種別。 */
+    public static final String APPLICENSE;
+
     private static final Class<?> THISCLASS;
     private static final Class<?> THISCLASS;
-    private static final String APPNAME;
-    private static final String APPVER;
-    private static final String APPLICENSE;
+
+    private static final PrintStream ERROUT = System.err;
 
     static{
         THISCLASS = Pmd2Xml.class;
 
     static{
         THISCLASS = Pmd2Xml.class;
@@ -47,7 +61,11 @@ public final class Pmd2Xml {
                 THISCLASS.getResourceAsStream("resources/version.properties");
         Properties verProps = new Properties();
         try{
                 THISCLASS.getResourceAsStream("resources/version.properties");
         Properties verProps = new Properties();
         try{
-            verProps.load(ver);
+            try{
+                verProps.load(ver);
+            }finally{
+                ver.close();
+            }
         }catch(IOException e){
             throw new ExceptionInInitializerError(e);
         }
         }catch(IOException e){
             throw new ExceptionInInitializerError(e);
         }
@@ -56,11 +74,12 @@ public final class Pmd2Xml {
         APPVER     = verProps.getProperty("app.version");
         APPLICENSE = verProps.getProperty("app.license");
 
         APPVER     = verProps.getProperty("app.version");
         APPLICENSE = verProps.getProperty("app.license");
 
-        Object dummy = new Pmd2Xml();
+        new Pmd2Xml().hashCode();
     }
 
     }
 
+
     /**
     /**
-     * 隠しコンストラクタ。
+     * ダミーコンストラクタ。
      */
     private Pmd2Xml(){
         super();
      */
     private Pmd2Xml(){
         super();
@@ -68,128 +87,49 @@ public final class Pmd2Xml {
         return;
     }
 
         return;
     }
 
+
     /**
     /**
-     * Mainエントリ
-     * @param args コマンドパラメータ
+     * VMを終了させる
+     * @param code 終了コード
      */
      */
-    public static void main(String[] args){
-        checkJRE();
-
-        String inputFile = null;
-        String outputFile = null;
-        boolean pmd2xml = false;
-        boolean xml2pmd = false;
-        boolean force = false;
-        int argsLen = args.length;
-        for(int argIdx = 0; argIdx < argsLen; argIdx++){
-            String arg = args[argIdx];
-
-            if(arg.equals("-h")){
-                putHelp();
-            }else if(arg.equals("-pmd2xml")){
-                pmd2xml = true;
-                xml2pmd = false;
-            }else if(arg.equals("-xml2pmd")){
-                pmd2xml = false;
-                xml2pmd = true;
-            }else if(arg.equals("-i")){
-                if(++argIdx >= argsLen){
-                    System.err.println("ERROR:");
-                    System.err.println("You need -i argument.");
-                    System.err.println("(-h for help)");
-                    System.exit(5);
-                }
-                inputFile = args[argIdx];
-            }else if(arg.equals("-o")){
-                if(++argIdx >= argsLen){
-                    System.err.println("ERROR:");
-                    System.err.println("You need -o argument.");
-                    System.err.println("(-h for help)");
-                    System.exit(5);
-                }
-                outputFile = args[argIdx];
-            }else if(arg.equals("-f")){
-                force = true;
-            }else{
-                System.err.println("ERROR:");
-                System.err.println("Unknown option:"+arg);
-                System.err.println("(-h for help)");
-                System.exit(5);
-            }
-        }
-
-        if( ( ! pmd2xml) && ( ! xml2pmd) ){
-            System.err.println("ERROR:");
-            System.err.println("You must specify -pmd2xml or -xml2pmd.");
-            System.err.println("(-h for help)");
-            System.exit(5);
-        }
-
-        if(inputFile == null){
-            System.err.println("ERROR:");
-            System.err.println("You must specify input file with -i.");
-            System.err.println("(-h for help)");
-            System.exit(5);
-        }
-
-        if(outputFile == null){
-            System.err.println("ERROR:");
-            System.err.println("You must specify output file with -o.");
-            System.err.println("(-h for help)");
-            System.exit(5);
-        }
+    private static void exit(int code){
+        System.exit(code);
+        return;
+    }
 
 
-        File iFile = new File(inputFile);
-        if( (! iFile.exists()) || (! iFile.isFile()) ){
-            System.err.println("ERROR:");
-            System.err.println("Can't find input file:"
-                    + iFile.getAbsolutePath());
-            System.err.println("(-h for help)");
-            System.exit(1);
-        }
+    /**
+     * 標準エラー出力へ例外情報出力。
+     * @param ex 例外
+     * @param dumpStack スタックトレースを出力するならtrue
+     */
+    private static void errPrintln(Throwable ex, boolean dumpStack){
+        String text = ex.toString();
+        ERROUT.println(text);
 
 
-        if( ! force ){
-            File oFile = new File(outputFile);
-            if(oFile.exists()){
-                System.err.println("ERROR:");
-                System.err.println(oFile.getAbsolutePath()
-                        + " already exists.");
-                System.err.println("If you want to overwrite, use -f.");
-                System.err.println("(-h for help)");
-                System.exit(1);
-            }
-        }else{
-            File oFile = new File(outputFile);
-            if(oFile.exists()){
-                if( ! oFile.isFile()){
-                    System.err.println("ERROR:");
-                    System.err.println(oFile.getAbsolutePath()
-                            + " is not file.");
-                    System.err.println("(-h for help)");
-                    System.exit(1);
-                }
-            }
+        if(dumpStack){
+            ex.printStackTrace(ERROUT);
         }
 
         }
 
-        try{
-            if(pmd2xml) pmd2xml(inputFile, outputFile);
-            else        xml2pmd(inputFile, outputFile);
-        }catch(IOException e){
-            ioError(e);
-        }catch(ParserConfigurationException e){
-            internalError(e);
-        }catch(IllegalPmdDataException e){
-            internalError(e);
-        }catch(MmdFormatException e){
-            pmdError(e);
-        }catch(TogaXmlException e){
-            xmlError(e);
-        }catch(SAXException e){
-            xmlError(e);
-        }
+        return;
+    }
 
 
-        System.exit(0);
+    /**
+     * 標準エラー出力へ例外情報出力。
+     * @param ex 例外
+     */
+    private static void errPrintln(Throwable ex){
+        errPrintln(ex, false);
+        return;
+    }
 
 
+    /**
+     * 共通エラーメッセージを出力する。
+     * @param text 個別メッセージ
+     */
+    private static void errMsg(String text){
+        ERROUT.println("ERROR:");
+        ERROUT.println(text);
+        ERROUT.println("(-h for help)");
         return;
     }
 
         return;
     }
 
@@ -199,8 +139,8 @@ public final class Pmd2Xml {
      * @param ex 例外
      */
     private static void ioError(Throwable ex){
      * @param ex 例外
      */
     private static void ioError(Throwable ex){
-        System.err.println(ex);
-        System.exit(1);
+        errPrintln(ex);
+        exit(EXIT_FILE);
     }
 
     /**
     }
 
     /**
@@ -209,8 +149,8 @@ public final class Pmd2Xml {
      * @param ex 例外
      */
     private static void xmlError(Throwable ex){
      * @param ex 例外
      */
     private static void xmlError(Throwable ex){
-        System.err.println(ex);
-        System.exit(2);
+        errPrintln(ex);
+        exit(EXIT_XML);
     }
 
     /**
     }
 
     /**
@@ -219,9 +159,8 @@ public final class Pmd2Xml {
      * @param ex 例外
      */
     private static void pmdError(Throwable ex){
      * @param ex 例外
      */
     private static void pmdError(Throwable ex){
-        System.err.println(ex);
-        ex.printStackTrace(System.err);
-        System.exit(3);
+        errPrintln(ex, true);
+        exit(EXIT_PMD);
     }
 
     /**
     }
 
     /**
@@ -230,9 +169,8 @@ public final class Pmd2Xml {
      * @param ex 例外
      */
     private static void internalError(Throwable ex){
      * @param ex 例外
      */
     private static void internalError(Throwable ex){
-        System.err.println(ex);
-        ex.printStackTrace(System.err);
-        System.exit(4);
+        errPrintln(ex, true);
+        exit(EXIT_INTERN);
     }
 
     /**
     }
 
     /**
@@ -242,8 +180,8 @@ public final class Pmd2Xml {
     private static void checkJRE(){
         Package jrePackage = java.lang.Object.class.getPackage();
         if( ! jrePackage.isCompatibleWith("1.6")){
     private static void checkJRE(){
         Package jrePackage = java.lang.Object.class.getPackage();
         if( ! jrePackage.isCompatibleWith("1.6")){
-            System.err.println("You need JRE 1.6 or later.");
-            System.exit(4);
+            ERROUT.println("You need JRE 1.6 or later.");
+            exit(EXIT_JREVER);
         }
         return;
     }
         }
         return;
     }
@@ -252,77 +190,20 @@ public final class Pmd2Xml {
      * ヘルプメッセージを出力してVMを終了させる。
      */
     private static void putHelp(){
      * ヘルプメッセージを出力してVMを終了させる。
      */
     private static void putHelp(){
-        System.err.println(APPNAME + ' ' + APPVER );
-        System.err.println("  License : " + APPLICENSE);
-        System.err.println("  http://mikutoga.sourceforge.jp/");
-        System.err.println();
-        System.err.println("-h       : put help massage");
-        System.err.println("-pmd2xml : convert *.pmd to *.xml");
-        System.err.println("-xml2pmd : convert *.xml to *.pmd");
-        System.err.println("-i file  : specify input file");
-        System.err.println("-o file  : specify output file");
-        System.err.println("-f       : force overwriting");
-        System.exit(0);
-        return;
-    }
+        StringBuilder appInfo = new StringBuilder();
+        String indent = "  ";
 
 
-    /**
-     * PMD->XML変換を行う。
-     * @param inputFile 入力ファイル名
-     * @param outputFile 出力ファイル名
-     * @throws IOException 入出力エラー
-     * @throws MmdFormatException 不正なPMDファイル
-     * @throws IllegalPmdDataException 不正なモデルデータ
-     */
-    private static void pmd2xml(String inputFile, String outputFile)
-            throws IOException, MmdFormatException, IllegalPmdDataException{
-        File iFile = new File(inputFile);
-        InputStream is = new FileInputStream(iFile);
-        is = new BufferedInputStream(is);
-        PmdModel model = pmdRead(is);
-        is.close();
+        appInfo.append(APPNAME).append(' ').append(APPVER)
+               .append('\n');
+        appInfo.append(indent)
+               .append("License").append(" : ").append(APPLICENSE)
+               .append('\n');
+        appInfo.append(indent)
+               .append("http://mikutoga.sourceforge.jp/")
+               .append('\n');
 
 
-        File oFile = new File(outputFile);
-        trunc(oFile);
-        OutputStream ostream;
-        ostream = new FileOutputStream(oFile, false);
-        ostream = new BufferedOutputStream(ostream);
-        xmlOut(model, ostream);
-        ostream.close();
-
-        return;
-    }
-
-    /**
-     * XML->PMD変換を行う。
-     * @param inputFile 入力ファイル名
-     * @param outputFile 出力ファイル名
-     * @throws IOException 入出力エラー
-     * @throws ParserConfigurationException XML構成のエラー
-     * @throws SAXException 不正なXMLファイル
-     * @throws TogaXmlException 不正なXMLファイル
-     * @throws IllegalPmdDataException 不正なPMDモデルデータ
-     */
-    private static void xml2pmd(String inputFile, String outputFile)
-            throws IOException,
-                   ParserConfigurationException,
-                   SAXException,
-                   TogaXmlException,
-                   IllegalPmdDataException {
-        File iFile = new File(inputFile);
-        InputStream is = new FileInputStream(iFile);
-        is = new BufferedInputStream(is);
-        InputSource source = new InputSource(is);
-        PmdModel model = xmlRead(source);
-        is.close();
-
-        File oFile = new File(outputFile);
-        trunc(oFile);
-        OutputStream ostream;
-        ostream = new FileOutputStream(oFile, false);
-        ostream = new BufferedOutputStream(ostream);
-        pmdOut(model, ostream);
-        ostream.close();
+        ERROUT.println(appInfo.toString());
+        ERROUT.println(OptInfo.getConsoleHelp());
 
         return;
     }
 
         return;
     }
@@ -347,73 +228,135 @@ public final class Pmd2Xml {
     }
 
     /**
     }
 
     /**
-     * PMDファイルからモデルデータを読み込む。
-     * @param is 入力ストリーム
-     * @return モデルデータ
-     * @throws IOException 入力エラー
-     * @throws MmdFormatException 不正なPMDファイルフォーマット
+     * 入力ストリームを準備する。
+     * @param fileName 入力ファイル名
+     * @return 入力ストリーム
      */
      */
-    private static PmdModel pmdRead(InputStream is)
-            throws IOException, MmdFormatException{
-        PmdLoader loader = new PmdLoader(is);
+    private static InputStream openInfile(String fileName){
+        File inFile = new File(fileName);
 
 
-        PmdModel model = loader.load();
+        if( (! inFile.exists()) || (! inFile.isFile()) ){
+            String absPath = inFile.getAbsolutePath();
+            errMsg("Can't find input file:" + absPath);
+            exit(EXIT_FILE);
+        }
 
 
-        return model;
-    }
+        InputStream is = null;
+        try{
+            is = new FileInputStream(inFile);
+        }catch(FileNotFoundException e){
+            ioError(e);
+            assert false;
+        }
 
 
-    /**
-     * XMLファイルからモデルデータを読み込む。
-     * @param source 入力ソース
-     * @return モデルデータ
-     * @throws IOException 入力エラー
-     * @throws ParserConfigurationException XML構成エラー
-     * @throws SAXException XML構文エラー
-     * @throws TogaXmlException 不正なXMLデータ
-     */
-    private static PmdModel xmlRead(InputSource source)
-            throws IOException,
-                   ParserConfigurationException,
-                   SAXException,
-                   TogaXmlException {
-        DocumentBuilder builder =
-                PmdXmlResources.newBuilder(XmlHandler.HANDLER);
-        Xml2PmdLoader loader = new Xml2PmdLoader(builder);
-
-        PmdModel model = loader.parse(source);
-
-        return model;
+        is = new BufferedInputStream(is);
+
+        return is;
     }
 
     /**
     }
 
     /**
-     * モデルデータをPMDファイルに出力する。
-     * @param model モデルデータ
-     * @param ostream 出力ストリーム
-     * @throws IOException 出力エラー
-     * @throws IllegalPmdDataException 不正なモデルデータ
+     * 出力ストリームを準備する。
+     * @param fileName 出力ファイル名
+     * @param overWrite 頭から上書きして良ければtrue
+     * @return 出力ストリーム
      */
      */
-    private static void pmdOut(PmdModel model, OutputStream ostream)
-            throws IOException, IllegalPmdDataException{
-        PmdExporter exporter = new PmdExporter(ostream);
-        exporter.dumpPmdModel(model);
-        ostream.close();
-        return;
+    private static OutputStream openOutfile(String fileName,
+                                            boolean overWrite) {
+        File outFile = new File(fileName);
+
+        if(outFile.exists()){
+            String absPath = outFile.getAbsolutePath();
+            if( ! outFile.isFile() ){
+                String msg = absPath + " is not file.";
+                errMsg(msg);
+                exit(EXIT_FILE);
+            }else if( ! overWrite ){
+                String msg =
+                          absPath + " already exists.\n"
+                        + "If you want to overwrite, use -f.";
+                errMsg(msg);
+                exit(EXIT_FILE);
+            }
+        }
+
+        try{
+            trunc(outFile);
+        }catch(IOException e){
+            ioError(e);
+        }
+
+        OutputStream os = null;
+        try{
+            os = new FileOutputStream(outFile);
+        }catch(FileNotFoundException e){
+            ioError(e);
+            assert false;
+        }
+
+        os = new BufferedOutputStream(os);
+
+        return os;
     }
 
     /**
     }
 
     /**
-     * モデルデータをXMLファイルに出力する。
-     * @param model モデルデータ
-     * @param ostream 出力ストリーム
-     * @throws IOException 出力エラー
-     * @throws IllegalPmdDataException 不正なモデルデータ
+     * Mainエントリ。
+     * @param args コマンドパラメータ
      */
      */
-    private static void xmlOut(PmdModel model, OutputStream ostream)
-            throws IOException, IllegalPmdDataException{
-        PmdXmlExporter exporter = new PmdXmlExporter(ostream);
-        exporter.setNewLine("\r\n");
-        exporter.setGenerator(APPNAME + ' ' + APPVER);
-        exporter.putPmdModel(model);
-        exporter.close();
+    public static void main(String[] args){
+        checkJRE();
+
+        Pmd2XmlConv converter = new Pmd2XmlConv();
+
+        OptInfo optInfo = OptInfo.parseOption(args);
+        if(optInfo.needHelp()){
+            putHelp();
+            exit(EXIT_OK);
+        }else if(optInfo.hasError()){
+            String optErrMsg = optInfo.getErrorMessage();
+            errMsg(optErrMsg);
+            exit(EXIT_OPT);
+        }
+
+        String inputFile = optInfo.getInFilename();
+        String outputFile = optInfo.getOutFilename();
+        boolean overwrite = optInfo.overwriteMode();
+
+        InputStream  is = openInfile(inputFile);
+        OutputStream os = openOutfile(outputFile, overwrite);
+
+        converter.setInType(optInfo.getInFileType());
+        converter.setOutType(optInfo.getOutFileType());
+
+        converter.setNewline(optInfo.getNewline());
+        converter.setGenerator(optInfo.getGenerator());
+
+        try{
+            converter.convert(is, os);
+        }catch(IOException e){
+            ioError(e);
+        }catch(IllegalPmdDataException e){
+            internalError(e);
+        }catch(MmdFormatException e){
+            pmdError(e);
+        }catch(TogaXmlException e){
+            xmlError(e);
+        }catch(SAXException e){
+            xmlError(e);
+        }
+
+        try{
+            is.close();
+            try{
+                os.close();
+            }catch(IOException e){
+                ioError(e);
+            }
+        }catch(IOException e){
+            ioError(e);
+        }
+
+        exit(EXIT_OK);
+
         return;
     }
 
         return;
     }
 
diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd2xml/Pmd2XmlConv.java b/src/main/java/jp/sourceforge/mikutoga/pmd2xml/Pmd2XmlConv.java
new file mode 100644 (file)
index 0000000..bd99da5
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ * pmd 2 xml converter
+ *
+ * License : The MIT License
+ * Copyright(c) 2010 MikuToga Partners
+ */
+
+package jp.sourceforge.mikutoga.pmd2xml;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.ParserConfigurationException;
+import jp.sourceforge.mikutoga.parser.MmdFormatException;
+import jp.sourceforge.mikutoga.pmd.IllegalPmdDataException;
+import jp.sourceforge.mikutoga.pmd.model.PmdModel;
+import jp.sourceforge.mikutoga.pmd.model.binio.PmdExporter;
+import jp.sourceforge.mikutoga.pmd.model.binio.PmdLoader;
+import jp.sourceforge.mikutoga.pmd.model.xml.PmdXmlExporter;
+import jp.sourceforge.mikutoga.pmd.model.xml.PmdXmlResources;
+import jp.sourceforge.mikutoga.pmd.model.xml.Xml2PmdLoader;
+import jp.sourceforge.mikutoga.xml.TogaXmlException;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+/**
+ * PMD-XML間コンバータ本体。
+ */
+public class Pmd2XmlConv {
+
+    private ModelFileTypes inTypes  = ModelFileTypes.NONE;
+    private ModelFileTypes outTypes = ModelFileTypes.NONE;
+    private String newLine = "\r\n";
+    private String generator = null;
+
+    private final DocumentBuilder builder;
+
+
+    /**
+     * コンストラクタ。
+     */
+    public Pmd2XmlConv(){
+        super();
+
+        try{
+            this.builder = PmdXmlResources.newBuilder(XmlHandler.HANDLER);
+        }catch(SAXException e){
+            throw new AssertionError(e);
+        }catch(ParserConfigurationException e){
+            throw new AssertionError(e);
+        }
+
+        return;
+    }
+
+
+    /**
+     * 入力ファイル種別を設定する。
+     * @param type ファイル種別
+     */
+    public void setInType(ModelFileTypes type){
+        if(type == null) throw new NullPointerException();
+        this.inTypes = type;
+        return;
+    }
+
+    /**
+     * 入力ファイル種別を返す。
+     * @return ファイル種別
+     */
+    public ModelFileTypes getInTypes(){
+        return this.inTypes;
+    }
+
+    /**
+     * 出力ファイル種別を設定する。
+     * @param type ファイル種別
+     */
+    public void setOutType(ModelFileTypes type){
+        if(type == null) throw new NullPointerException();
+        this.outTypes = type;
+        return;
+    }
+
+    /**
+     * 出力ファイル種別を返す。
+     * @return ファイル種別
+     */
+    public ModelFileTypes getOutTypes(){
+        return this.outTypes;
+    }
+
+    /**
+     * XML出力用改行文字列を設定する。
+     * @param newline 改行文字
+     */
+    public void setNewline(String newline){
+        this.newLine = newline;
+        return;
+    }
+
+    /**
+     * XML出力用改行文字列を返す。
+     * @return 改行文字
+     */
+    public String getNewLine(){
+        return this.newLine;
+    }
+
+    /**
+     * ジェネレータ名を設定する。
+     * @param generator ジェネレータ名。表示したくない場合はnull
+     */
+    public void setGenerator(String generator){
+        this.generator = generator;
+        return;
+    }
+
+    /**
+     * ジェネレータ名を返す。
+     * @return ジェネレータ名。非表示の場合はnullを返す。
+     */
+    public String getGenerator(){
+        return this.generator;
+    }
+
+    /**
+     * ファイル変換を行う。
+     * @param is 入力ストリーム
+     * @param os 出力ストリーム
+     * @throws IOException 入力エラー
+     * @throws MmdFormatException フォーマットエラー
+     * @throws SAXException XMLエラー
+     * @throws TogaXmlException XMLエラー
+     * @throws IllegalPmdDataException 内部エラー
+     */
+    public void convert(InputStream is, OutputStream os)
+            throws IOException,
+                   MmdFormatException,
+                   SAXException,
+                   TogaXmlException,
+                   IllegalPmdDataException {
+        PmdModel model = readModel(is);
+        writeModel(model, os);
+        return;
+    }
+
+    /**
+     * モデルファイルを読み込む。
+     * @param is 入力ストリーム
+     * @return モデルデータ
+     * @throws IOException 入力エラー
+     * @throws MmdFormatException フォーマットエラー
+     * @throws SAXException XMLエラー
+     * @throws TogaXmlException XMLエラー
+     */
+    public PmdModel readModel(InputStream is)
+            throws IOException,
+                   MmdFormatException,
+                   SAXException,
+                   TogaXmlException {
+        PmdModel model = null;
+        switch(this.inTypes){
+        case PMD:
+            model = pmdRead(is);
+            break;
+        case XML_101009:
+            model = xmlRead(is);
+            break;
+        default:
+            assert false;
+            break;
+        }
+        return model;
+    }
+
+    /**
+     * モデルファイルを出力する。
+     * @param model モデルデータ
+     * @param os 出力ストリーム
+     * @throws IOException 出力エラー
+     * @throws IllegalPmdDataException データの不備
+     */
+    public void writeModel(PmdModel model, OutputStream os)
+            throws IOException,
+                   IllegalPmdDataException {
+        switch(this.outTypes){
+        case PMD:
+            pmdOut(model, os);
+            break;
+        case XML_101009:
+            xmlOut(model, os);
+            break;
+        default:
+            assert false;
+            break;
+        }
+    }
+
+    /**
+     * PMDファイルからモデルデータを読み込む。
+     * @param is 入力ストリーム
+     * @return モデルデータ
+     * @throws IOException 入力エラー
+     * @throws MmdFormatException 不正なPMDファイルフォーマット
+     */
+    private PmdModel pmdRead(InputStream is)
+            throws IOException, MmdFormatException{
+        PmdLoader loader = new PmdLoader(is);
+        PmdModel model = loader.load();
+        return model;
+    }
+
+    /**
+     * XMLファイルからモデルデータを読み込む。
+     * @param is 入力ストリーム
+     * @return モデルデータ
+     * @throws IOException 入力エラー
+     * @throws SAXException XML構文エラー
+     * @throws TogaXmlException 不正なXMLデータ
+     */
+    private PmdModel xmlRead(InputStream is)
+            throws IOException,
+                   SAXException,
+                   TogaXmlException {
+        InputSource source = new InputSource(is);
+        PmdModel result = xmlRead(source);
+        return result;
+    }
+
+    /**
+     * XMLファイルからモデルデータを読み込む。
+     * @param source 入力ソース
+     * @return モデルデータ
+     * @throws IOException 入力エラー
+     * @throws SAXException XML構文エラー
+     * @throws TogaXmlException 不正なXMLデータ
+     */
+    private PmdModel xmlRead(InputSource source)
+            throws IOException,
+                   SAXException,
+                   TogaXmlException {
+        Xml2PmdLoader loader = new Xml2PmdLoader(this.builder);
+        PmdModel model = loader.parse(source);
+        return model;
+    }
+
+    /**
+     * モデルデータをPMDファイルに出力する。
+     * @param model モデルデータ
+     * @param ostream 出力ストリーム
+     * @throws IOException 出力エラー
+     * @throws IllegalPmdDataException 不正なモデルデータ
+     */
+    private void pmdOut(PmdModel model, OutputStream ostream)
+            throws IOException, IllegalPmdDataException{
+        PmdExporter exporter = new PmdExporter(ostream);
+        exporter.dumpPmdModel(model);
+        ostream.close();
+        return;
+    }
+
+    /**
+     * モデルデータをXMLファイルに出力する。
+     * @param model モデルデータ
+     * @param ostream 出力ストリーム
+     * @throws IOException 出力エラー
+     * @throws IllegalPmdDataException 不正なモデルデータ
+     */
+    private void xmlOut(PmdModel model, OutputStream ostream)
+            throws IOException, IllegalPmdDataException{
+        PmdXmlExporter exporter = new PmdXmlExporter(ostream);
+        exporter.setNewLine(this.newLine);
+        exporter.setGenerator(this.generator);
+        exporter.putPmdModel(model);
+        exporter.close();
+        return;
+    }
+
+}
diff --git a/src/test/java/testdata/CnvAssert.java b/src/test/java/testdata/CnvAssert.java
new file mode 100644 (file)
index 0000000..83ca380
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ */
+
+package testdata;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import jp.sourceforge.mikutoga.pmd2xml.ModelFileTypes;
+import jp.sourceforge.mikutoga.pmd2xml.Pmd2XmlConv;
+
+import static org.junit.Assert.*;
+
+/**
+ *
+ */
+public class CnvAssert {
+
+    /**
+     * テスト出力用テンポラリファイルの生成。
+     * テスト終了時(VM終了時)に消える。
+     * @return テンポラリファイル
+     * @throws IOException エラー
+     */
+    public static File openTempFile() throws IOException{
+        File file = File.createTempFile("pmd2xml", null);
+        file.deleteOnExit();
+        return file;
+    }
+
+    /**
+     * XMLリソースをPMDに変換した結果がPMDリソースに等しいと表明する。
+     * @param klass リソース元クラス
+     * @param xmlResource XMLリソース名
+     * @param expPmdResource PMDリソース名
+     * @throws Exception エラー
+     */
+    public static void assertXml2Pmd(
+            Class<?> klass,
+            String xmlResource,
+            String expPmdResource )
+            throws Exception{
+        InputStream xmlis =
+                klass.getResourceAsStream(xmlResource);
+        assertNotNull(xmlis);
+        xmlis = new BufferedInputStream(xmlis);
+
+        File destFile = openTempFile();
+        OutputStream destOut;
+        destOut = new FileOutputStream(destFile);
+        destOut = new BufferedOutputStream(destOut);
+
+        Pmd2XmlConv converter = new Pmd2XmlConv();
+        converter.setInType(ModelFileTypes.XML_101009);
+        converter.setOutType(ModelFileTypes.PMD);
+        converter.setNewline("\n");
+
+        converter.convert(xmlis, destOut);
+
+        xmlis.close();
+        destOut.close();
+
+        assertSameFile(klass, expPmdResource, destFile);
+
+        return;
+    }
+
+    /**
+     * PMDリソースをXMLに変換した結果がXMLリソースに等しいと表明する。
+     * @param klass リソース元クラス
+     * @param pmdResource PMDリソース名
+     * @param expXmlResource XMLリソース名
+     * @throws Exception エラー
+     */
+    public static void assertPmd2Xml(
+            Class<?> klass,
+            String pmdResource,
+            String expXmlResource )
+            throws Exception{
+        InputStream pmdis =
+                klass.getResourceAsStream(pmdResource);
+        assertNotNull(pmdis);
+        pmdis = new BufferedInputStream(pmdis);
+
+        File destFile = openTempFile();
+        OutputStream destOut;
+        destOut = new FileOutputStream(destFile);
+        destOut = new BufferedOutputStream(destOut);
+
+        Pmd2XmlConv converter = new Pmd2XmlConv();
+        converter.setInType(ModelFileTypes.PMD);
+        converter.setOutType(ModelFileTypes.XML_101009);
+        converter.setNewline("\n");
+        converter.setGenerator(null);
+
+        converter.convert(pmdis, destOut);
+
+        pmdis.close();
+        destOut.close();
+
+        assertSameFile(klass, expXmlResource, destFile);
+
+        return;
+    }
+
+    /**
+     * リソースとファイルの内容が等しいと表明する。
+     * @param klass リソース元クラス
+     * @param resourceName リソース名
+     * @param resFile ファイル
+     * @throws IOException 入力エラー
+     */
+    public static void assertSameFile(
+            Class<?> klass,
+            String resourceName,
+            File resFile )
+            throws IOException{
+        InputStream expis =
+                klass.getResourceAsStream(resourceName);
+        assertNotNull(expis);
+
+        InputStream resIn = new FileInputStream(resFile);
+
+        try{
+            assertSameStream(expis, resIn);
+        }finally{
+            expis.close();
+            resIn.close();
+        }
+
+        return;
+    }
+
+    /**
+     * 2つの入力ストリーム内容が等しいと表明する。
+     * @param expIn 期待する入力ストリーム
+     * @param resIn 結果入力ストリーム
+     * @throws IOException 入力エラー
+     */
+    public static void assertSameStream(InputStream expIn, InputStream resIn)
+            throws IOException{
+        InputStream expis = new BufferedInputStream(expIn);
+        InputStream resis = new BufferedInputStream(resIn);
+
+
+        for(;;){
+            int expCh = expis.read();
+            int resCh = resis.read();
+
+            assertEquals(expCh, resCh);
+
+            if(expCh < 0) break;
+        }
+
+        return;
+    }
+
+}
diff --git a/src/test/java/testdata/charset/CharsetTest.java b/src/test/java/testdata/charset/CharsetTest.java
new file mode 100644 (file)
index 0000000..1185976
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ */
+
+package testdata.charset;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import static testdata.CnvAssert.*;
+
+/**
+ *
+ */
+public class CharsetTest {
+
+    static Class<?> THISCLASS = CharsetTest.class;
+
+    public CharsetTest() {
+        assert this.getClass() == THISCLASS;
+        return;
+    }
+
+    @BeforeClass
+    public static void setUpClass() {
+    }
+
+    @AfterClass
+    public static void tearDownClass() {
+    }
+
+    @Before
+    public void setUp() {
+    }
+
+    @After
+    public void tearDown() {
+    }
+
+    @Test
+    public void pmd2xml() throws Exception{
+        System.out.println("pmd2xml");
+        assertPmd2Xml(THISCLASS, "test.pmd", "result.xml");
+        return;
+    }
+
+    @Test
+    public void xml2pmd() throws Exception{
+        System.out.println("xml2pmd");
+        assertXml2Pmd(THISCLASS, "source.xml", "test.pmd");
+        return;
+    }
+
+}
diff --git a/src/test/java/testdata/minimum/MinimumTest.java b/src/test/java/testdata/minimum/MinimumTest.java
new file mode 100644 (file)
index 0000000..485b204
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ */
+
+package testdata.minimum;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import static testdata.CnvAssert.*;
+
+/**
+ *
+ */
+public class MinimumTest {
+
+    static Class<?> THISCLASS = MinimumTest.class;
+
+    public MinimumTest() {
+        assert this.getClass() == THISCLASS;
+        return;
+    }
+
+    @BeforeClass
+    public static void setUpClass() {
+    }
+
+    @AfterClass
+    public static void tearDownClass() {
+    }
+
+    @Before
+    public void setUp() {
+    }
+
+    @After
+    public void tearDown() {
+    }
+
+    @Test
+    public void pmd2xml() throws Exception{
+        System.out.println("pmd2xml");
+        assertPmd2Xml(THISCLASS, "test.pmd", "test.xml");
+        return;
+    }
+
+    @Test
+    public void xml2pmd() throws Exception{
+        System.out.println("xml2pmd");
+        assertXml2Pmd(THISCLASS, "test.xml", "test.pmd");
+        return;
+    }
+
+}
diff --git a/src/test/resources/testdata/charset/Readme.txt b/src/test/resources/testdata/charset/Readme.txt
new file mode 100644 (file)
index 0000000..4a4691b
--- /dev/null
@@ -0,0 +1 @@
+文字集合、エンコーディング、XML文字エスケープ、空白文字に関するテストデータ。
diff --git a/src/test/resources/testdata/charset/result.xml b/src/test/resources/testdata/charset/result.xml
new file mode 100644 (file)
index 0000000..68d22ee
--- /dev/null
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!--
+  MikuMikuDance
+    model-data(*.pmd) on XML
+-->
+
+
+<!-- a A  9 亜'" -->
+<pmdModel
+  xmlns="http://mikutoga.sourceforge.jp/xml/ns/pmdxml/101009"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://mikutoga.sourceforge.jp/xml/ns/pmdxml/101009 http://mikutoga.sourceforge.jp/xml/xsd/pmdxml-101009.xsd"
+  schemaVersion="101009"
+
+  name="a A &#x20;9&#x3000;&#x4E9C;&apos;&quot;"
+>
+
+
+<description>
+azAZ&#xFF21;&#xFF3A;09&#xFF10;&#xFF19;&#x3042;&#x30A2;&#xFF71;&#x3091;&#x30F5;&#x30F6;&#x03A9;&#x03C9;&#x0416;&#x0436;<br/>
+&#x2510;&#x2514;&#x6F22;&#x5CE0;&#x51DC;&#x7199;<br/>
+&#x30F4;&#x30A6;&#x309B;&#xFF73;&#xFF9E;&#x30D1;&#x30CF;&#x309C;&#xFF8A;&#xFF9F;<br/>
+&#x58FA;&#x58F7;&#x5C2D;&#x582F;<br/>
+\&#xFF3C;\&#xFFE5;<br/>
+&#x9AD9;&#x2468;&#x2252;&#x2235;&#xFFE2;&#x3231;&#x03A3;<br/>
+#$[\]^{|}~<br/>
+cdata&apos;&quot;&amp;&lt;&gt;test<br/>
+&lt;!--fake comment--&gt;<br/>
+A B &#x20;C&#x09;D&#x09;&#x09;E&#x3000;F&#x3000;&#x3000;GHI
+</description>
+<!--
+azAZAZ0909あアアゑヵヶΩωЖж
+┐└漢峠凜熙
+ヴウ゛ヴパハ゜パ
+壺壷尭堯
+\\\¥
+髙⑨≒∵¬㈱Σ
+#$[\]^{|}~
+cdata'"&<>test
+<!- -fake comment- ->
+A B  C␉D␉␉E F  GHI
+-->
+
+<license>
+</license>
+
+<credits>
+</credits>
+
+<meta name="siteURL" content="" />
+<meta name="imageURL" content="" />
+
+<materialList>
+
+</materialList>
+
+<toonMap>
+  <toonDef toonFileId="tf0" index="0" winFileName="toon01.bmp" />
+  <toonDef toonFileId="tf1" index="1" winFileName="toon02.bmp" />
+  <toonDef toonFileId="tf2" index="2" winFileName="toon03.bmp" />
+  <toonDef toonFileId="tf3" index="3" winFileName="toon04.bmp" />
+  <toonDef toonFileId="tf4" index="4" winFileName="toon05.bmp" />
+  <toonDef toonFileId="tf5" index="5" winFileName="toon06.bmp" />
+  <toonDef toonFileId="tf6" index="6" winFileName="toon07.bmp" />
+  <toonDef toonFileId="tf7" index="7" winFileName="toon08.bmp" />
+  <toonDef toonFileId="tf8" index="8" winFileName="toon09.bmp" />
+  <toonDef toonFileId="tf9" index="9" winFileName="toon10.bmp" />
+</toonMap>
+
+<boneList>
+
+<!--
+Bone types:
+[0 : ROTATE      : Rotate       : 回転           :]
+[1 : ROTMOV      : Rotate/Move  : 回転/移動      :]
+[2 : IK          : IK           : IK             :]
+[3 : UNKNOWN     : Unknown      : 不明           :]
+[4 : UNDERIK     : Under IK     : IK影響下(回転) :]
+[5 : UNDERROT    : Under rotate : 回転影響下     :]
+[6 : IKCONNECTED : IK connected : IK接続先       :]
+[7 : HIDDEN      : Hidden       : 非表示         :]
+[8 : TWIST       : Twist        : 捩り           :]
+[9 : LINKEDROT   : Linked Rotate: 回転連動       :]
+-->
+
+</boneList>
+
+<boneGroupList>
+
+</boneGroupList>
+
+<ikChainList>
+
+</ikChainList>
+
+<morphList>
+
+<!--
+Morph types:
+[1 : EYEBROW : まゆ   ]
+[2 : EYE     : 目     ]
+[3 : LIP     : リップ ]
+[4 : EXTRA   : その他 ]
+-->
+
+</morphList>
+
+<rigidList>
+
+<!--
+Rigid behavior types:
+[0 : FOLLOWBONE    : ボーン追従       ]
+[1 : ONLYDYNAMICS  : 物理演算         ]
+[2 : BONEDDYNAMICS : ボーン位置合わせ ]
+-->
+
+</rigidList>
+
+<rigidGroupList>
+
+  <rigidGroup rigidGroupId="rg1" />
+
+  <rigidGroup rigidGroupId="rg2" />
+
+  <rigidGroup rigidGroupId="rg3" />
+
+  <rigidGroup rigidGroupId="rg4" />
+
+  <rigidGroup rigidGroupId="rg5" />
+
+  <rigidGroup rigidGroupId="rg6" />
+
+  <rigidGroup rigidGroupId="rg7" />
+
+  <rigidGroup rigidGroupId="rg8" />
+
+  <rigidGroup rigidGroupId="rg9" />
+
+  <rigidGroup rigidGroupId="rg10" />
+
+  <rigidGroup rigidGroupId="rg11" />
+
+  <rigidGroup rigidGroupId="rg12" />
+
+  <rigidGroup rigidGroupId="rg13" />
+
+  <rigidGroup rigidGroupId="rg14" />
+
+  <rigidGroup rigidGroupId="rg15" />
+
+  <rigidGroup rigidGroupId="rg16" />
+
+</rigidGroupList>
+
+<jointList>
+
+</jointList>
+
+<surfaceGroupList>
+
+</surfaceGroupList>
+
+<vertexList>
+
+</vertexList>
+
+</pmdModel>
+
+<!-- EOF -->
diff --git a/src/test/resources/testdata/charset/source.xml b/src/test/resources/testdata/charset/source.xml
new file mode 100644 (file)
index 0000000..0f80ccc
--- /dev/null
@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!--
+  MikuMikuDance
+    model-data(*.pmd) on XML
+-->
+
+
+<pmdModel
+  xmlns="http://mikutoga.sourceforge.jp/xml/ns/pmdxml/101009"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://mikutoga.sourceforge.jp/xml/ns/pmdxml/101009 http://mikutoga.sourceforge.jp/xml/xsd/pmdxml-101009.xsd"
+  schemaVersion="101009"
+
+  name="a A  9 亜&apos;&quot;"
+>
+
+
+<description>
+azAZAZ0909あアアゑヵヶΩωЖж<br/>
+┐└漢峠凜熙<br/>
+ヴウ゛ヴパハ゜パ<br/>
+壺壷尭堯<br/>
+\\¥¥<br/>
+髙⑨≒∵¬㈱Σ<br/>
+#$[\]^{|}~<br/>
+<![CDATA[cdata'"&<>test]]><br/>
+<![CDATA[<!--fake comment-->]]><br/>
+A B  C&#x09;D&#x09;&#x09;E F  G
+H&#x0A;I
+</description>
+
+<license>
+</license>
+
+<credits>
+</credits>
+
+<meta name="siteURL" content="" />
+<meta name="imageURL" content="" />
+
+<materialList>
+
+</materialList>
+
+<toonMap>
+  <toonDef toonFileId="tf0" index="0" winFileName="toon01.bmp" />
+  <toonDef toonFileId="tf1" index="1" winFileName="toon02.bmp" />
+  <toonDef toonFileId="tf2" index="2" winFileName="toon03.bmp" />
+  <toonDef toonFileId="tf3" index="3" winFileName="toon04.bmp" />
+  <toonDef toonFileId="tf4" index="4" winFileName="toon05.bmp" />
+  <toonDef toonFileId="tf5" index="5" winFileName="toon06.bmp" />
+  <toonDef toonFileId="tf6" index="6" winFileName="toon07.bmp" />
+  <toonDef toonFileId="tf7" index="7" winFileName="toon08.bmp" />
+  <toonDef toonFileId="tf8" index="8" winFileName="toon09.bmp" />
+  <toonDef toonFileId="tf9" index="9" winFileName="toon10.bmp" />
+</toonMap>
+
+<boneList>
+
+<!--
+Bone types:
+[0 : ROTATE      : Rotate       : 回転           :]
+[1 : ROTMOV      : Rotate/Move  : 回転/移動      :]
+[2 : IK          : IK           : IK             :]
+[3 : UNKNOWN     : Unknown      : 不明           :]
+[4 : UNDERIK     : Under IK     : IK影響下(回転) :]
+[5 : UNDERROT    : Under rotate : 回転影響下     :]
+[6 : IKCONNECTED : IK connected : IK接続先       :]
+[7 : HIDDEN      : Hidden       : 非表示         :]
+[8 : TWIST       : Twist        : 捩り           :]
+[9 : LINKEDROT   : Linked Rotate: 回転連動       :]
+-->
+
+</boneList>
+
+<boneGroupList>
+
+</boneGroupList>
+
+<ikChainList>
+
+</ikChainList>
+
+<morphList>
+
+<!--
+Morph types:
+[1 : EYEBROW : まゆ   ]
+[2 : EYE     : 目     ]
+[3 : LIP     : リップ ]
+[4 : EXTRA   : その他 ]
+-->
+
+</morphList>
+
+<rigidList>
+
+<!--
+Rigid behavior types:
+[0 : FOLLOWBONE    : ボーン追従       ]
+[1 : ONLYDYNAMICS  : 物理演算         ]
+[2 : BONEDDYNAMICS : ボーン位置合わせ ]
+-->
+
+</rigidList>
+
+<rigidGroupList>
+
+  <rigidGroup rigidGroupId="rg1" />
+
+  <rigidGroup rigidGroupId="rg2" />
+
+  <rigidGroup rigidGroupId="rg3" />
+
+  <rigidGroup rigidGroupId="rg4" />
+
+  <rigidGroup rigidGroupId="rg5" />
+
+  <rigidGroup rigidGroupId="rg6" />
+
+  <rigidGroup rigidGroupId="rg7" />
+
+  <rigidGroup rigidGroupId="rg8" />
+
+  <rigidGroup rigidGroupId="rg9" />
+
+  <rigidGroup rigidGroupId="rg10" />
+
+  <rigidGroup rigidGroupId="rg11" />
+
+  <rigidGroup rigidGroupId="rg12" />
+
+  <rigidGroup rigidGroupId="rg13" />
+
+  <rigidGroup rigidGroupId="rg14" />
+
+  <rigidGroup rigidGroupId="rg15" />
+
+  <rigidGroup rigidGroupId="rg16" />
+
+</rigidGroupList>
+
+<jointList>
+
+</jointList>
+
+<surfaceGroupList>
+
+</surfaceGroupList>
+
+<vertexList>
+
+</vertexList>
+
+</pmdModel>
+
+<!-- EOF -->
diff --git a/src/test/resources/testdata/charset/test.pmd b/src/test/resources/testdata/charset/test.pmd
new file mode 100644 (file)
index 0000000..6cb6d5d
Binary files /dev/null and b/src/test/resources/testdata/charset/test.pmd differ
diff --git a/src/test/resources/testdata/minimum/Readme.txt b/src/test/resources/testdata/minimum/Readme.txt
new file mode 100644 (file)
index 0000000..7fd068b
--- /dev/null
@@ -0,0 +1,7 @@
+最小構成のPMDファイル。
+※ XMLの改行コードはLF。ジェネレータ名は非表示。
+
+名前は空
+説明文は空
+頂点0
+ボーン0
diff --git a/src/test/resources/testdata/minimum/test.pmd b/src/test/resources/testdata/minimum/test.pmd
new file mode 100644 (file)
index 0000000..c2fb71c
Binary files /dev/null and b/src/test/resources/testdata/minimum/test.pmd differ
diff --git a/src/test/resources/testdata/minimum/test.xml b/src/test/resources/testdata/minimum/test.xml
new file mode 100644 (file)
index 0000000..2c33970
--- /dev/null
@@ -0,0 +1,148 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!--
+  MikuMikuDance
+    model-data(*.pmd) on XML
+-->
+
+
+<!--  -->
+<pmdModel
+  xmlns="http://mikutoga.sourceforge.jp/xml/ns/pmdxml/101009"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://mikutoga.sourceforge.jp/xml/ns/pmdxml/101009 http://mikutoga.sourceforge.jp/xml/xsd/pmdxml-101009.xsd"
+  schemaVersion="101009"
+
+  name=""
+>
+
+
+<description>
+</description>
+
+<license>
+</license>
+
+<credits>
+</credits>
+
+<meta name="siteURL" content="" />
+<meta name="imageURL" content="" />
+
+<materialList>
+
+</materialList>
+
+<toonMap>
+  <toonDef toonFileId="tf0" index="0" winFileName="toon01.bmp" />
+  <toonDef toonFileId="tf1" index="1" winFileName="toon02.bmp" />
+  <toonDef toonFileId="tf2" index="2" winFileName="toon03.bmp" />
+  <toonDef toonFileId="tf3" index="3" winFileName="toon04.bmp" />
+  <toonDef toonFileId="tf4" index="4" winFileName="toon05.bmp" />
+  <toonDef toonFileId="tf5" index="5" winFileName="toon06.bmp" />
+  <toonDef toonFileId="tf6" index="6" winFileName="toon07.bmp" />
+  <toonDef toonFileId="tf7" index="7" winFileName="toon08.bmp" />
+  <toonDef toonFileId="tf8" index="8" winFileName="toon09.bmp" />
+  <toonDef toonFileId="tf9" index="9" winFileName="toon10.bmp" />
+</toonMap>
+
+<boneList>
+
+<!--
+Bone types:
+[0 : ROTATE      : Rotate       : 回転           :]
+[1 : ROTMOV      : Rotate/Move  : 回転/移動      :]
+[2 : IK          : IK           : IK             :]
+[3 : UNKNOWN     : Unknown      : 不明           :]
+[4 : UNDERIK     : Under IK     : IK影響下(回転) :]
+[5 : UNDERROT    : Under rotate : 回転影響下     :]
+[6 : IKCONNECTED : IK connected : IK接続先       :]
+[7 : HIDDEN      : Hidden       : 非表示         :]
+[8 : TWIST       : Twist        : 捩り           :]
+[9 : LINKEDROT   : Linked Rotate: 回転連動       :]
+-->
+
+</boneList>
+
+<boneGroupList>
+
+</boneGroupList>
+
+<ikChainList>
+
+</ikChainList>
+
+<morphList>
+
+<!--
+Morph types:
+[1 : EYEBROW : まゆ   ]
+[2 : EYE     : 目     ]
+[3 : LIP     : リップ ]
+[4 : EXTRA   : その他 ]
+-->
+
+</morphList>
+
+<rigidList>
+
+<!--
+Rigid behavior types:
+[0 : FOLLOWBONE    : ボーン追従       ]
+[1 : ONLYDYNAMICS  : 物理演算         ]
+[2 : BONEDDYNAMICS : ボーン位置合わせ ]
+-->
+
+</rigidList>
+
+<rigidGroupList>
+
+  <rigidGroup rigidGroupId="rg1" />
+
+  <rigidGroup rigidGroupId="rg2" />
+
+  <rigidGroup rigidGroupId="rg3" />
+
+  <rigidGroup rigidGroupId="rg4" />
+
+  <rigidGroup rigidGroupId="rg5" />
+
+  <rigidGroup rigidGroupId="rg6" />
+
+  <rigidGroup rigidGroupId="rg7" />
+
+  <rigidGroup rigidGroupId="rg8" />
+
+  <rigidGroup rigidGroupId="rg9" />
+
+  <rigidGroup rigidGroupId="rg10" />
+
+  <rigidGroup rigidGroupId="rg11" />
+
+  <rigidGroup rigidGroupId="rg12" />
+
+  <rigidGroup rigidGroupId="rg13" />
+
+  <rigidGroup rigidGroupId="rg14" />
+
+  <rigidGroup rigidGroupId="rg15" />
+
+  <rigidGroup rigidGroupId="rg16" />
+
+</rigidGroupList>
+
+<jointList>
+
+</jointList>
+
+<surfaceGroupList>
+
+</surfaceGroupList>
+
+<vertexList>
+
+</vertexList>
+
+</pmdModel>
+
+<!-- EOF -->