OSDN Git Service

SAX対応
authorOlyutorskii <olyutorskii@users.osdn.me>
Sat, 8 Jun 2013 04:30:29 +0000 (13:30 +0900)
committerOlyutorskii <olyutorskii@users.osdn.me>
Sat, 8 Jun 2013 04:30:29 +0000 (13:30 +0900)
57 files changed:
pom.xml
src/main/config/checks.xml
src/main/java/jp/sfjp/mikutoga/pmd/model/RigidGroup.java
src/main/java/jp/sfjp/mikutoga/pmd/model/ToonMap.java
src/main/java/jp/sfjp/mikutoga/pmd/model/binio/BoneBuilder.java [moved from src/main/java/jp/sfjp/mikutoga/pmd/binio/BoneBuilder.java with 99% similarity]
src/main/java/jp/sfjp/mikutoga/pmd/model/binio/JointBuilder.java [moved from src/main/java/jp/sfjp/mikutoga/pmd/binio/JointBuilder.java with 99% similarity]
src/main/java/jp/sfjp/mikutoga/pmd/model/binio/MaterialBuilder.java [moved from src/main/java/jp/sfjp/mikutoga/pmd/binio/MaterialBuilder.java with 99% similarity]
src/main/java/jp/sfjp/mikutoga/pmd/model/binio/MorphBuilder.java [moved from src/main/java/jp/sfjp/mikutoga/pmd/binio/MorphBuilder.java with 99% similarity]
src/main/java/jp/sfjp/mikutoga/pmd/model/binio/PmdExporter.java [moved from src/main/java/jp/sfjp/mikutoga/pmd/binio/PmdExporter.java with 94% similarity]
src/main/java/jp/sfjp/mikutoga/pmd/model/binio/PmdExporterBase.java [moved from src/main/java/jp/sfjp/mikutoga/pmd/binio/PmdExporterBase.java with 99% similarity]
src/main/java/jp/sfjp/mikutoga/pmd/model/binio/PmdExporterExt1.java [moved from src/main/java/jp/sfjp/mikutoga/pmd/binio/PmdExporterExt1.java with 99% similarity]
src/main/java/jp/sfjp/mikutoga/pmd/model/binio/PmdExporterExt2.java [moved from src/main/java/jp/sfjp/mikutoga/pmd/binio/PmdExporterExt2.java with 98% similarity]
src/main/java/jp/sfjp/mikutoga/pmd/model/binio/PmdExporterExt3.java [moved from src/main/java/jp/sfjp/mikutoga/pmd/binio/PmdExporterExt3.java with 99% similarity]
src/main/java/jp/sfjp/mikutoga/pmd/model/binio/PmdLoader.java [moved from src/main/java/jp/sfjp/mikutoga/pmd/binio/PmdLoader.java with 98% similarity]
src/main/java/jp/sfjp/mikutoga/pmd/model/binio/RigidBuilder.java [moved from src/main/java/jp/sfjp/mikutoga/pmd/binio/RigidBuilder.java with 99% similarity]
src/main/java/jp/sfjp/mikutoga/pmd/model/binio/ShapeBuilder.java [moved from src/main/java/jp/sfjp/mikutoga/pmd/binio/ShapeBuilder.java with 99% similarity]
src/main/java/jp/sfjp/mikutoga/pmd/model/binio/TextBuilder.java [moved from src/main/java/jp/sfjp/mikutoga/pmd/binio/TextBuilder.java with 99% similarity]
src/main/java/jp/sfjp/mikutoga/pmd/model/binio/ToonBuilder.java [moved from src/main/java/jp/sfjp/mikutoga/pmd/binio/ToonBuilder.java with 97% similarity]
src/main/java/jp/sfjp/mikutoga/pmd/model/binio/package-info.java [moved from src/main/java/jp/sfjp/mikutoga/pmd/binio/package-info.java with 81% similarity]
src/main/java/jp/sfjp/mikutoga/pmd/model/xml/CloseXmlMark.java [new file with mode: 0644]
src/main/java/jp/sfjp/mikutoga/pmd/model/xml/ExporterBone.java [new file with mode: 0644]
src/main/java/jp/sfjp/mikutoga/pmd/model/xml/ExporterDynamics.java [new file with mode: 0644]
src/main/java/jp/sfjp/mikutoga/pmd/model/xml/ExporterMaterial.java [new file with mode: 0644]
src/main/java/jp/sfjp/mikutoga/pmd/model/xml/ExporterMorph.java [new file with mode: 0644]
src/main/java/jp/sfjp/mikutoga/pmd/model/xml/ExporterShape.java [new file with mode: 0644]
src/main/java/jp/sfjp/mikutoga/pmd/model/xml/ExtraExporter.java [new file with mode: 0644]
src/main/java/jp/sfjp/mikutoga/pmd/model/xml/OpenXmlMark.java [new file with mode: 0644]
src/main/java/jp/sfjp/mikutoga/pmd/model/xml/PmdAttr.java [new file with mode: 0644]
src/main/java/jp/sfjp/mikutoga/pmd/model/xml/PmdTag.java [new file with mode: 0644]
src/main/java/jp/sfjp/mikutoga/pmd/model/xml/PmdXmlExporter.java [new file with mode: 0644]
src/main/java/jp/sfjp/mikutoga/pmd/model/xml/RefHelper.java [new file with mode: 0644]
src/main/java/jp/sfjp/mikutoga/pmd/model/xml/SaxBoneListener.java [new file with mode: 0644]
src/main/java/jp/sfjp/mikutoga/pmd/model/xml/SaxDynamicsListener.java [new file with mode: 0644]
src/main/java/jp/sfjp/mikutoga/pmd/model/xml/SaxListener.java [new file with mode: 0644]
src/main/java/jp/sfjp/mikutoga/pmd/model/xml/SaxMaterialListener.java [new file with mode: 0644]
src/main/java/jp/sfjp/mikutoga/pmd/model/xml/SaxModelListener.java [new file with mode: 0644]
src/main/java/jp/sfjp/mikutoga/pmd/model/xml/SaxMorphListener.java [new file with mode: 0644]
src/main/java/jp/sfjp/mikutoga/pmd/model/xml/SaxShapeListener.java [new file with mode: 0644]
src/main/java/jp/sfjp/mikutoga/pmd/model/xml/Schema101009.java [moved from src/main/java/jp/sfjp/mikutoga/pmd/xml/Schema101009.java with 95% similarity]
src/main/java/jp/sfjp/mikutoga/pmd/model/xml/Schema130128.java [moved from src/main/java/jp/sfjp/mikutoga/pmd/xml/Schema130128.java with 95% similarity]
src/main/java/jp/sfjp/mikutoga/pmd/model/xml/XmlHandler.java [new file with mode: 0644]
src/main/java/jp/sfjp/mikutoga/pmd/model/xml/XmlModelFileType.java [moved from src/main/java/jp/sfjp/mikutoga/pmd/xml/XmlModelFileType.java with 94% similarity]
src/main/java/jp/sfjp/mikutoga/pmd/model/xml/XmlPmdLoader.java [new file with mode: 0644]
src/main/java/jp/sfjp/mikutoga/pmd/model/xml/package-info.java [moved from src/main/java/jp/sfjp/mikutoga/pmd/xml/package-info.java with 54% similarity]
src/main/java/jp/sfjp/mikutoga/pmd/xml/PmdXmlExporter.java [deleted file]
src/main/java/jp/sfjp/mikutoga/pmd/xml/XmlLoader.java [deleted file]
src/main/java/jp/sfjp/mikutoga/pmd2xml/ModelFileType.java
src/main/java/jp/sfjp/mikutoga/pmd2xml/Pmd2Xml.java
src/main/java/jp/sfjp/mikutoga/pmd2xml/Pmd2XmlConv.java
src/main/java/jp/sfjp/mikutoga/pmd2xml/XmlInputUtil.java [new file with mode: 0644]
src/main/resources/jp/sfjp/mikutoga/pmd/model/xml/resources/pmdxml-101009.xsd [moved from src/main/resources/jp/sfjp/mikutoga/pmd/xml/resources/pmdxml-101009.xsd with 100% similarity]
src/main/resources/jp/sfjp/mikutoga/pmd/model/xml/resources/pmdxml-130128.xsd [moved from src/main/resources/jp/sfjp/mikutoga/pmd/xml/resources/pmdxml-130128.xsd with 100% similarity]
src/test/java/jp/sfjp/mikutoga/pmd2xml/ModelFileTypeTest.java
src/test/java/testdata/CnvAssert.java
src/test/java/testdata/DebugOutputStream.java [new file with mode: 0644]
src/test/resources/testdata/pmd101009/rigid/allrigid.xml
src/test/resources/testdata/pmd130128/rigid/allrigid.xml

diff --git a/pom.xml b/pom.xml
index c867323..643fea3 100644 (file)
--- a/pom.xml
+++ b/pom.xml
         <dependency>
             <groupId>jp.sourceforge.mikutoga</groupId>
             <artifactId>togagem</artifactId>
-            <version>2.102.5-SNAPSHOT</version>
+            <version>3.101.5-SNAPSHOT</version>
             <scope>compile</scope>
         </dependency>
 
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-enforcer-plugin</artifactId>
-                <version>1.2</version>
+                <version>1.3.1</version>
                 <configuration>
                     <rules>
                         <requireMavenVersion>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-install-plugin</artifactId>
-                <version>2.4</version>
+                <version>2.5.1</version>
             </plugin>
 
             <plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-surefire-plugin</artifactId>
-                <version>2.14.1</version>
+                <version>2.16</version>
                 <configuration>
                     <skipTests>false</skipTests>
                     <enableAssertions>true</enableAssertions>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-checkstyle-plugin</artifactId>
-                <version>2.10</version>
+                <version>2.11</version>
                 <!-- config from property value -->
             </plugin>
 
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-javadoc-plugin</artifactId>
-                <version>2.9</version>
+                <version>2.9.1</version>
                 <configuration>
                     <skip>false</skip>
                     <notimestamp>true</notimestamp>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-surefire-report-plugin</artifactId>
-                <version>2.14.1</version>
+                <version>2.16</version>
                 <configuration>
                     <showSuccess>false</showSuccess>
                 </configuration>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-checkstyle-plugin</artifactId>
-                <version>2.10</version>
+                <version>2.11</version>
                 <configuration>
                     <skip>false</skip>
                     <!-- config from property value -->
index 3d6d80e..3e31cbb 100644 (file)
         <module name="RegexpSinglelineJava" />
 -->
 
-<!-- バグ?
-        <module name="RequireThis" />
--->
-
 <!-- Obsolated
         <module name="TabCharacter" />
 -->
index 7399b4a..0aaee86 100644 (file)
@@ -17,6 +17,9 @@ import java.util.List;
  */
 public class RigidGroup implements SerialNumbered, Iterable<RigidInfo> {
 
+    public static final int MAX_RIGID_GROUP = 16;
+
+
     private final List<RigidInfo> rigidList = new ArrayList<RigidInfo>();
 
     private int serialNo = -1;
index d809a58..37f928a 100644 (file)
@@ -25,6 +25,7 @@ import java.util.TreeMap;
  */
 public class ToonMap {
 
+    public static final int MAX_CUSTOM_TOON = 10;
     private static final Map<Integer, String> DEF_TOONMAP;
 
     static{
@@ -43,6 +44,8 @@ public class ToonMap {
         map.put(0xff, "toon0.bmp");
 
         DEF_TOONMAP = Collections.unmodifiableMap(map);
+
+        assert DEF_TOONMAP.size() == MAX_CUSTOM_TOON + 1;
     }
 
     private final Map<Integer, String> toonMap =
@@ -85,8 +88,8 @@ public class ToonMap {
      * @return 等しければtrue
      */
     public boolean isDefaultMap(){
-        if(this.toonMap.equals(DEF_TOONMAP)) return true;
-        return false;
+        boolean result = this.toonMap.equals(DEF_TOONMAP);
+        return result;
     }
 
     /**
@@ -99,9 +102,10 @@ public class ToonMap {
         if(thisToon == null) return false;
 
         String defToon = DEF_TOONMAP.get(idx);
-        if(thisToon.equals(defToon)) return true;
 
-        return false;
+        boolean result = thisToon.equals(defToon);
+
+        return result;
     }
 
     /**
@@ -5,7 +5,7 @@
  * Copyright(c) 2010 MikuToga Partners
  */
 
-package jp.sfjp.mikutoga.pmd.binio;
+package jp.sfjp.mikutoga.pmd.model.binio;
 
 import java.util.Iterator;
 import java.util.LinkedList;
@@ -5,7 +5,7 @@
  * Copyright(c) 2010 MikuToga Partners
  */
 
-package jp.sfjp.mikutoga.pmd.binio;
+package jp.sfjp.mikutoga.pmd.model.binio;
 
 import java.util.Iterator;
 import java.util.List;
@@ -5,7 +5,7 @@
  * Copyright(c) 2010 MikuToga Partners
  */
 
-package jp.sfjp.mikutoga.pmd.binio;
+package jp.sfjp.mikutoga.pmd.model.binio;
 
 import java.awt.Color;
 import java.util.Iterator;
@@ -5,7 +5,7 @@
  * Copyright(c) 2010 MikuToga Partners
  */
 
-package jp.sfjp.mikutoga.pmd.binio;
+package jp.sfjp.mikutoga.pmd.model.binio;
 
 import java.util.ArrayList;
 import java.util.Iterator;
@@ -5,7 +5,7 @@
  * Copyright(c) 2010 MikuToga Partners
  */
 
-package jp.sfjp.mikutoga.pmd.binio;
+package jp.sfjp.mikutoga.pmd.model.binio;
 
 import java.io.OutputStream;
 
@@ -5,7 +5,7 @@
  * Copyright(c) 2010 MikuToga Partners
  */
 
-package jp.sfjp.mikutoga.pmd.binio;
+package jp.sfjp.mikutoga.pmd.model.binio;
 
 import java.awt.Color;
 import java.io.IOException;
@@ -5,7 +5,7 @@
  * Copyright(c) 2010 MikuToga Partners
  */
 
-package jp.sfjp.mikutoga.pmd.binio;
+package jp.sfjp.mikutoga.pmd.model.binio;
 
 import java.io.IOException;
 import java.io.OutputStream;
@@ -5,7 +5,7 @@
  * Copyright(c) 2010 MikuToga Partners
  */
 
-package jp.sfjp.mikutoga.pmd.binio;
+package jp.sfjp.mikutoga.pmd.model.binio;
 
 import java.io.IOException;
 import java.io.OutputStream;
@@ -5,7 +5,7 @@
  * Copyright(c) 2010 MikuToga Partners
  */
 
-package jp.sfjp.mikutoga.pmd.binio;
+package jp.sfjp.mikutoga.pmd.model.binio;
 
 import java.io.IOException;
 import java.io.OutputStream;
@@ -5,7 +5,7 @@
  * Copyright(c) 2010 MikuToga Partners
  */
 
-package jp.sfjp.mikutoga.pmd.binio;
+package jp.sfjp.mikutoga.pmd.model.binio;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -5,7 +5,7 @@
  * Copyright(c) 2010 MikuToga Partners
  */
 
-package jp.sfjp.mikutoga.pmd.binio;
+package jp.sfjp.mikutoga.pmd.model.binio;
 
 import java.util.Iterator;
 import java.util.List;
@@ -5,7 +5,7 @@
  * Copyright(c) 2010 MikuToga Partners
  */
 
-package jp.sfjp.mikutoga.pmd.binio;
+package jp.sfjp.mikutoga.pmd.model.binio;
 
 import java.util.Iterator;
 import java.util.List;
@@ -5,7 +5,7 @@
  * Copyright(c) 2010 MikuToga Partners
  */
 
-package jp.sfjp.mikutoga.pmd.binio;
+package jp.sfjp.mikutoga.pmd.model.binio;
 
 import java.util.Iterator;
 import java.util.List;
@@ -5,7 +5,7 @@
  * Copyright(c) 2010 MikuToga Partners
  */
 
-package jp.sfjp.mikutoga.pmd.binio;
+package jp.sfjp.mikutoga.pmd.model.binio;
 
 import jp.sfjp.mikutoga.bin.parser.ParseStage;
 import jp.sfjp.mikutoga.pmd.PmdConst;
@@ -9,6 +9,6 @@
  * PMDモデルファイル(*.pmd)用入出力。
  */
 
-package jp.sfjp.mikutoga.pmd.binio;
+package jp.sfjp.mikutoga.pmd.model.binio;
 
 /* EOF */
diff --git a/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/CloseXmlMark.java b/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/CloseXmlMark.java
new file mode 100644 (file)
index 0000000..09a9023
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * annotation for ending XML-element
+ *
+ * License : The MIT License
+ * Copyright(c) 2013 MikuToga Partners
+ */
+
+package jp.sfjp.mikutoga.pmd.model.xml;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * XML要素終了通知用注釈。
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@interface CloseXmlMark {
+    /** タグ指定。 */
+    PmdTag value();
+}
diff --git a/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/ExporterBone.java b/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/ExporterBone.java
new file mode 100644 (file)
index 0000000..8dce9ea
--- /dev/null
@@ -0,0 +1,373 @@
+/*
+ * bone xml exporter
+ *
+ * License : The MIT License
+ * Copyright(c) 2013 MikuToga Partners
+ */
+
+package jp.sfjp.mikutoga.pmd.model.xml;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Locale;
+import jp.sfjp.mikutoga.corelib.I18nText;
+import jp.sfjp.mikutoga.math.MkPos3D;
+import jp.sfjp.mikutoga.pmd.BoneType;
+import jp.sfjp.mikutoga.pmd.model.BoneGroup;
+import jp.sfjp.mikutoga.pmd.model.BoneInfo;
+import jp.sfjp.mikutoga.pmd.model.IKChain;
+import jp.sfjp.mikutoga.pmd.model.PmdModel;
+import jp.sfjp.mikutoga.xml.ProxyXmlExporter;
+
+/**
+ * ボーン設定のXMLエクスポーター。
+ */
+class ExporterBone extends ProxyXmlExporter {
+
+    private static final Locale DEF_LOCALE = Locale.JAPANESE;
+
+    private static final String LEAD_REF = "Ref:";
+    private static final String BONETYPE_COMMENT =
+          "Bone types:\n"
+        + "[0 : ROTATE      : Rotate       : 回転           :]\n"
+        + "[1 : ROTMOV      : Rotate/Move  : 回転/移動      :]\n"
+        + "[2 : IK          : IK           : IK             :]\n"
+        + "[3 : UNKNOWN     : Unknown      : 不明           :]\n"
+        + "[4 : UNDERIK     : Under IK     : IK影響下(回転) :]\n"
+        + "[5 : UNDERROT    : Under rotate : 回転影響下     :]\n"
+        + "[6 : IKCONNECTED : IK connected : IK接続先       :]\n"
+        + "[7 : HIDDEN      : Hidden       : 非表示         :]\n"
+        + "[8 : TWIST       : Twist        : 捩り           :]\n"
+        + "[9 : LINKEDROT   : Linked Rotate: 回転連動       :]\n";
+
+
+    private final ExtraExporter exp;
+    private XmlModelFileType xmlType = XmlModelFileType.XML_130128;
+
+
+    /**
+     * コンストラクタ。
+     * @param delegate 委譲先
+     */
+    ExporterBone(PmdXmlExporter delegate) {
+        super(delegate);
+        this.exp = new ExtraExporter(delegate);
+        return;
+    }
+
+    /**
+     * 出力XMLファイル種別を設定する。
+     * @param type ファイル種別
+     */
+    void setXmlFileType(XmlModelFileType type){
+        this.xmlType = type;
+        return;
+    }
+
+    /**
+     * ボーンリストを出力する。
+     * @param model モデルデータ
+     * @throws IOException 出力エラー
+     */
+    void putBoneList(PmdModel model)
+            throws IOException{
+        ind().putSimpleSTag(PmdTag.BONE_LIST.tag()).ln();
+        pushNest();
+
+        boolean dumped = false;
+        for(BoneInfo bone : model.getBoneList()){
+            if( ! dumped ){
+                ln().putBlockComment(BONETYPE_COMMENT).ln();
+            }
+            putBone(bone);
+            dumped = true;
+        }
+
+        popNest();
+        ind().putETag(PmdTag.BONE_LIST.tag()).ln(2);
+
+        return;
+    }
+
+    /**
+     * 個別のボーン情報を出力する。
+     * @param bone ボーン情報
+     * @throws IOException 出力エラー
+     */
+    private void putBone(BoneInfo bone)
+            throws IOException{
+        putBoneComment(bone);
+
+        ind().putOpenSTag(PmdTag.BONE.tag()).sp();
+        I18nText i18nName = bone.getBoneName();
+        this.exp.putPrimaryNameAttr(PmdAttr.NAME, i18nName);
+        sp();
+        this.exp.putNumberedIdAttr(PmdAttr.BONE_ID,
+                ExtraExporter.PFX_BONE, bone);
+        sp();
+        BoneType type = bone.getBoneType();
+        putAttr(PmdAttr.TYPE.attr(), type.name());
+        sp().putCloseSTag().ln();
+        pushNest();
+
+        this.exp.putI18nName(i18nName);
+
+        MkPos3D position = bone.getPosition();
+        ind();
+        this.exp.putPosition(position);
+        ln();
+
+        BoneInfo srcBone = bone.getSrcBone();
+        if(bone.getBoneType() == BoneType.LINKEDROT){
+            putRotationRatio(bone);
+        }else if(srcBone != null){
+            putSourceBone(srcBone);
+        }
+
+        BoneInfo prev = bone.getPrevBone();
+        BoneInfo next = bone.getNextBone();
+        putBoneChain(prev, next);
+
+        popNest();
+        ind().putETag(PmdTag.BONE.tag()).ln(2);
+
+        return;
+    }
+
+    /**
+     * ボーンコメントを出力する。
+     * @param bone ボーン
+     * @throws IOException 出力エラー
+     */
+    private void putBoneComment(BoneInfo bone)
+            throws IOException{
+        StringBuilder boneComment = new StringBuilder();
+
+        I18nText i18nName = bone.getBoneName();
+        String boneName = i18nName.getText();
+        if(boneName.isEmpty()){
+            boneName = "[NAMELESS]";
+        }
+        boneComment.append(boneName);
+
+        BoneType type = bone.getBoneType();
+        String typeName = type.getGuiName(DEF_LOCALE);
+        boneComment.append("\u0020[").append(typeName).append(']');
+        ind().putLineComment(boneComment.toString()).ln();
+
+        return;
+    }
+
+    /**
+     * ボーン回転連動率を出力する。
+     * @param bone ボーン
+     * @throws IOException 出力エラー
+     */
+    private void putRotationRatio(BoneInfo bone)
+            throws IOException{
+        ind().putOpenSTag(PmdTag.ROTATION_RATIO.tag()).sp();
+        putIntAttr(PmdAttr.RATIO.attr(),
+                bone.getRotationRatio()).sp();
+        putCloseEmpty().ln();
+
+        return;
+    }
+
+    /**
+     * 元ボーン情報を出力する。
+     * @param source 元ボーン
+     * @throws IOException 出力エラー
+     */
+    private void putSourceBone(BoneInfo source)
+            throws IOException{
+
+
+        String iktag;
+        switch(this.xmlType){
+        case XML_101009:
+            iktag = PmdTag.IK_BONE.tag();
+            break;
+        case XML_130128:
+            iktag = PmdTag.SOURCE_BONE.tag();
+            break;
+        default:
+            assert false;
+            throw new AssertionError();
+        }
+
+        ind().putOpenSTag(iktag).sp();
+        this.exp.putNumberedIdAttr(PmdAttr.BONE_IDREF,
+                ExtraExporter.PFX_BONE, source);
+        sp();
+        putCloseEmpty().sp();
+
+        String ikBoneName = LEAD_REF + source.getBoneName().getText();
+        putLineComment(ikBoneName);
+        ln();
+
+        return;
+    }
+
+    /**
+     * ボーン間チェーン情報を出力する。
+     * @param prev 前ボーン
+     * @param next 次ボーン
+     * @throws IOException 出力エラー
+     */
+    private void putBoneChain(BoneInfo prev, BoneInfo next)
+            throws IOException{
+        StringBuilder chainComment = new StringBuilder();
+
+        if(prev != null){
+            chainComment.append('[')
+                        .append(prev.getBoneName().getPrimaryText())
+                        .append("]\u0020>>#");
+        }
+        if(next != null){
+            if(chainComment.length() <= 0) chainComment.append('#');
+            chainComment.append(">>\u0020[")
+                        .append(next.getBoneName().getPrimaryText())
+                        .append(']');
+        }
+        if(chainComment.length() > 0){
+            ln();
+            ind().putLineComment(chainComment).ln();
+        }
+
+        ind().putOpenSTag(PmdTag.BONE_CHAIN.tag());
+        if(prev != null){
+            sp();
+            this.exp.putNumberedIdAttr(PmdAttr.PREV_BONE_IDREF,
+                    ExtraExporter.PFX_BONE, prev);
+        }
+        if(next != null){
+            sp();
+            this.exp.putNumberedIdAttr(PmdAttr.NEXT_BONE_IDREF,
+                    ExtraExporter.PFX_BONE, next);
+        }
+        sp().putCloseEmpty().ln();
+
+        return;
+    }
+
+    /**
+     * ボーングループリストを出力する。
+     * @param model モデルデータ
+     * @throws IOException 出力エラー
+     */
+    void putBoneGroupList(PmdModel model)
+            throws IOException{
+        ind().putSimpleSTag(PmdTag.BONE_GROUP_LIST.tag()).ln();
+
+        pushNest();
+        boolean dumped = false;
+        List<BoneGroup> groupList = model.getBoneGroupList();
+        for(BoneGroup group : groupList){
+            if(group.isDefaultBoneGroup()) continue;
+            if( ! dumped ) ln();
+            putBoneGroup(group);
+            dumped = true;
+        }
+        popNest();
+
+        ind().putETag(PmdTag.BONE_GROUP_LIST.tag()).ln(2);
+
+        return;
+    }
+
+    /**
+     * 個別のボーングループ情報を出力する。
+     * @param group ボーングループ情報
+     * @throws IOException 出力エラー
+     */
+    private void putBoneGroup(BoneGroup group)
+            throws IOException{
+        I18nText i18nName = group.getGroupName();
+
+        this.exp.putLocalNameComment(i18nName);
+        ln();
+        ind().putOpenSTag(PmdTag.BONE_GROUP.tag()).sp();
+        this.exp.putPrimaryNameAttr(PmdAttr.NAME, i18nName);
+        sp().putCloseSTag().ln();
+        pushNest();
+
+        this.exp.putI18nName(i18nName);
+
+        for(BoneInfo bone : group){
+            ind().putOpenSTag(PmdTag.BONE_GROUP_MEMBER.tag()).sp();
+            this.exp.putNumberedIdAttr(PmdAttr.BONE_IDREF,
+                    ExtraExporter.PFX_BONE, bone);
+            sp();
+            putCloseEmpty().sp();
+            String boneName = LEAD_REF + bone.getBoneName().getText();
+            putLineComment(boneName).ln();
+        }
+
+        popNest();
+        ind().putETag(PmdTag.BONE_GROUP.tag()).ln(2);
+
+        return;
+    }
+
+    /**
+     * IKチェーンリストを出力する。
+     * @param model モデルデータ
+     * @throws IOException 出力エラー
+     */
+    void putIKChainList(PmdModel model)
+            throws IOException{
+        ind().putSimpleSTag(PmdTag.IK_CHAIN_LIST.tag()).ln();
+
+        pushNest();
+        boolean dumped = false;
+        List<IKChain> chainList = model.getIKChainList();
+        for(IKChain chain : chainList){
+            if( ! dumped ) ln();
+            putIKChain(chain);
+            dumped = true;
+        }
+        popNest();
+
+        ind().putETag(PmdTag.IK_CHAIN_LIST.tag()).ln(2);
+
+        return;
+    }
+
+    /**
+     * 個別のIKチェーン情報を出力する。
+     * @param chain チェーン情報
+     * @throws IOException 出力エラー
+     */
+    private void putIKChain(IKChain chain)
+            throws IOException{
+        int depth = chain.getIKDepth();
+        float weight = chain.getIKWeight();
+        BoneInfo ikBone = chain.getIkBone();
+
+        ind().putLineComment(LEAD_REF + ikBone.getBoneName().getText()).ln();
+        ind().putOpenSTag(PmdTag.IK_CHAIN.tag()).sp();
+        this.exp.putNumberedIdAttr(PmdAttr.IK_BONE_IDREF,
+                ExtraExporter.PFX_BONE, ikBone);
+        sp();
+        putIntAttr(PmdAttr.RECURSIVE_DEPTH.attr(), depth).sp();
+        putFloatAttr(PmdAttr.WEIGHT.attr(), weight);
+        sp().putCloseSTag().ln();
+        pushNest();
+
+        for(BoneInfo bone : chain){
+            ind().putOpenSTag(PmdTag.CHAIN_ORDER.tag()).sp();
+            this.exp.putNumberedIdAttr(PmdAttr.BONE_IDREF,
+                    ExtraExporter.PFX_BONE, bone);
+            sp();
+            putCloseEmpty().sp();
+            putLineComment(LEAD_REF + bone.getBoneName().getText());
+            ln();
+        }
+
+        popNest();
+        ind().putETag(PmdTag.IK_CHAIN.tag()).ln(2);
+
+        return;
+    }
+
+}
diff --git a/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/ExporterDynamics.java b/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/ExporterDynamics.java
new file mode 100644 (file)
index 0000000..8ca5534
--- /dev/null
@@ -0,0 +1,460 @@
+/*
+ * dynamics xml exporter
+ *
+ * License : The MIT License
+ * Copyright(c) 2013 MikuToga Partners
+ */
+
+package jp.sfjp.mikutoga.pmd.model.xml;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+import jp.sfjp.mikutoga.corelib.I18nText;
+import jp.sfjp.mikutoga.math.MkPos3D;
+import jp.sfjp.mikutoga.pmd.Deg3d;
+import jp.sfjp.mikutoga.pmd.Rad3d;
+import jp.sfjp.mikutoga.pmd.RigidShapeType;
+import jp.sfjp.mikutoga.pmd.TripletRange;
+import jp.sfjp.mikutoga.pmd.model.BoneInfo;
+import jp.sfjp.mikutoga.pmd.model.DynamicsInfo;
+import jp.sfjp.mikutoga.pmd.model.JointInfo;
+import jp.sfjp.mikutoga.pmd.model.PmdModel;
+import jp.sfjp.mikutoga.pmd.model.RigidGroup;
+import jp.sfjp.mikutoga.pmd.model.RigidInfo;
+import jp.sfjp.mikutoga.pmd.model.RigidShape;
+import jp.sfjp.mikutoga.xml.ProxyXmlExporter;
+
+/**
+ * 剛体力学設定のXMLエクスポーター。
+ */
+class ExporterDynamics extends ProxyXmlExporter {
+
+    private static final String LEAD_REF = "Ref:";
+    private static final String RIGIDBEHAVIOR_COMMENT =
+          "Rigid behavior types:\n"
+        + "[0 : FOLLOWBONE    : ボーン追従       ]\n"
+        + "[1 : ONLYDYNAMICS  : 物理演算         ]\n"
+        + "[2 : BONEDDYNAMICS : ボーン位置合わせ ]\n";
+
+
+    private final ExtraExporter exp;
+
+
+    /**
+     * コンストラクタ。
+     * @param delegate 委譲先
+     */
+    ExporterDynamics(PmdXmlExporter delegate) {
+        super(delegate);
+        this.exp = new ExtraExporter(delegate);
+        return;
+    }
+
+    /**
+     * 剛体リストを出力する。
+     * @param model モデルデータ
+     * @throws IOException 出力エラー
+     */
+    void putRigidList(PmdModel model)
+            throws IOException{
+        ind().putSimpleSTag(PmdTag.RIGID_LIST.tag()).ln();
+        pushNest();
+
+        boolean dumped = false;
+        for(RigidInfo rigid : model.getRigidList()){
+            if( ! dumped ){
+                ln().putBlockComment(RIGIDBEHAVIOR_COMMENT).ln();
+            }
+            putRigid(rigid);
+            dumped = true;
+        }
+
+        popNest();
+        ind().putETag(PmdTag.RIGID_LIST.tag()).ln(2);
+
+        return;
+    }
+
+    /**
+     * 個別の剛体情報を出力する。
+     * @param rigid 剛体情報
+     * @throws IOException 出力エラー
+     */
+    private void putRigid(RigidInfo rigid) throws IOException{
+        I18nText i18nName = rigid.getRigidName();
+        String primary = i18nName.getPrimaryText();
+
+        this.exp.putLocalNameComment(i18nName);
+        ln();
+
+        ind().putOpenSTag(PmdTag.RIGID.tag()).sp();
+        putAttr(PmdAttr.NAME.attr(), primary).sp();
+        this.exp.putNumberedIdAttr(PmdAttr.RIGID_ID,
+                ExtraExporter.PFX_RIGID, rigid);
+        sp();
+        putAttr(PmdAttr.BEHAVIOR.attr(),
+                rigid.getBehaviorType().name());
+        sp().putCloseSTag().ln();
+        pushNest();
+
+        this.exp.putI18nName(i18nName);
+        putRigidBody(rigid);
+
+        popNest();
+        ind().putETag(PmdTag.RIGID.tag()).ln(2);
+
+        return;
+    }
+
+    /**
+     * 剛体の詳細を出力する。
+     * @param rigid 剛体情報
+     * @throws IOException 出力エラー
+     */
+    private void putRigidBody(RigidInfo rigid) throws IOException{
+        BoneInfo linkedBone = rigid.getLinkedBone();
+        putLinkedBone(linkedBone);
+
+        RigidShape shape = rigid.getRigidShape();
+        putRigidShape(shape);
+
+        MkPos3D position = rigid.getPosition();
+        ind();
+        this.exp.putPosition(position);
+        ln();
+
+        Rad3d rotation = rigid.getRotation();
+        ind();
+        this.exp.putRadRotation(rotation);
+        ln();
+
+        DynamicsInfo dynamics = rigid.getDynamicsInfo();
+        putDynamics(dynamics);
+        ln();
+
+        Collection<RigidGroup> throughGroup = rigid.getThroughGroupColl();
+        putThroughRigid(throughGroup);
+
+        return;
+    }
+
+    /**
+     * 剛体接続ボーンを出力する。
+     * @param linkedBone 接続ボーン
+     * @throws IOException 出力エラー
+     */
+    private void putLinkedBone(BoneInfo linkedBone) throws IOException{
+        if(linkedBone == null) return;
+
+        ind().putOpenSTag(PmdTag.LINKED_BONE.tag()).sp();
+        this.exp.putNumberedIdAttr(PmdAttr.BONE_IDREF,
+                ExtraExporter.PFX_BONE,
+                linkedBone);
+        sp();
+        putCloseEmpty();
+        sp().putLineComment(LEAD_REF + linkedBone.getBoneName().getText());
+        ln(2);
+
+        return;
+    }
+
+    /**
+     * 剛体形状を出力する。
+     * @param shape 剛体形状
+     * @throws IOException 出力エラー
+     */
+    private void putRigidShape(RigidShape shape) throws IOException{
+        RigidShapeType type = shape.getShapeType();
+
+        switch(type){
+        case BOX:
+            ind().putOpenSTag(PmdTag.RIGID_SHAPE_BOX.tag()).sp();
+            putFloatAttr(PmdAttr.WIDTH.attr(),
+                    shape.getWidth()).sp();
+            putFloatAttr(PmdAttr.HEIGHT.attr(),
+                    shape.getHeight()).sp();
+            putFloatAttr(PmdAttr.DEPTH.attr(),
+                    shape.getDepth()).sp();
+            break;
+        case SPHERE:
+            ind().putOpenSTag(PmdTag.RIGID_SHAPE_SPHERE.tag()).sp();
+            putFloatAttr(PmdAttr.RADIUS.attr(),
+                    shape.getRadius()).sp();
+            break;
+        case CAPSULE:
+            ind().putOpenSTag(PmdTag.RIGID_SHAPE_CAPSULE.tag()).sp();
+            putFloatAttr(PmdAttr.HEIGHT.attr(),
+                    shape.getHeight()).sp();
+            putFloatAttr(PmdAttr.RADIUS.attr(),
+                    shape.getRadius()).sp();
+            break;
+        default:
+            assert false;
+            throw new AssertionError();
+        }
+
+        putCloseEmpty().ln();
+
+        return;
+    }
+
+    /**
+     * 力学設定を出力する。
+     * @param dynamics 力学設定
+     * @throws IOException 出力エラー
+     */
+    private void putDynamics(DynamicsInfo dynamics)
+            throws IOException{
+        ind().putOpenSTag(PmdTag.DYNAMICS.tag()).ln();
+        pushNest();
+        ind().putFloatAttr(PmdAttr.MASS.attr(),
+                dynamics.getMass()).ln();
+        ind().putFloatAttr(PmdAttr.DAMPING_POSITION.attr(),
+                dynamics.getDampingPosition()).ln();
+        ind().putFloatAttr(PmdAttr.DAMPING_ROTATION.attr(),
+                dynamics.getDampingRotation()).ln();
+        ind().putFloatAttr(PmdAttr.RESTITUTION.attr(),
+                dynamics.getRestitution()).ln();
+        ind().putFloatAttr(PmdAttr.FRICTION.attr(),
+                dynamics.getFriction()).ln();
+        popNest();
+        ind().putCloseEmpty().ln();
+
+        return;
+    }
+
+    /**
+     * 通過剛体グループを出力する。
+     * @param groupColl 通過剛体グループ群
+     * @throws IOException 出力エラー
+     */
+    private void putThroughRigid(Collection<RigidGroup> groupColl)
+            throws IOException{
+        for(RigidGroup group : groupColl){
+            ind().putOpenSTag(PmdTag.THROUGH_RIGID_GROUP.tag()).sp();
+            this.exp
+                    .putNumberedIdAttr(PmdAttr.RIGID_GROUP_IDREF,
+                              ExtraExporter.PFX_RIGIDGROUP,
+                              group.getSerialNumber() + 1);
+            sp();
+            putCloseEmpty().ln();
+        }
+
+        return;
+    }
+
+    /**
+     * 剛体グループリストを出力する。
+     * @param model モデルデータ
+     * @throws IOException 出力エラー
+     */
+    void putRigidGroupList(PmdModel model)
+            throws IOException{
+        ind().putSimpleSTag(PmdTag.RIGID_GROUP_LIST.tag()).ln(2);
+        pushNest();
+
+        boolean singleLast = false;
+        for(RigidGroup group : model.getRigidGroupList()){
+            List<RigidInfo> rigidList = group.getRigidList();
+            if(singleLast &&  ! rigidList.isEmpty()){
+                ln();
+            }
+            ind().putOpenSTag(PmdTag.RIGID_GROUP.tag()).sp();
+            this.exp.putNumberedIdAttr(PmdAttr.RIGID_GROUP_ID,
+                              ExtraExporter.PFX_RIGIDGROUP,
+                              group.getSerialNumber() + 1);
+            sp();
+            if(rigidList.isEmpty()){
+                putCloseEmpty().ln();
+                singleLast = true;
+                continue;
+            }
+            putCloseSTag().ln();
+            pushNest();
+
+            for(RigidInfo rigid : rigidList){
+                ind().putOpenSTag(PmdTag.RIGID_GROUP_MEMBER.tag()).sp();
+                this.exp.putNumberedIdAttr(PmdAttr.RIGID_IDREF,
+                        ExtraExporter.PFX_RIGID, rigid);
+                sp();
+                putCloseEmpty();
+                sp();
+                putLineComment(LEAD_REF + rigid.getRigidName().getText());
+                ln();
+            }
+
+            popNest();
+            ind().putETag(PmdTag.RIGID_GROUP.tag()).ln(2);
+            singleLast = false;
+        }
+
+        if(singleLast){
+            ln();
+        }
+
+        popNest();
+        ind().putETag(PmdTag.RIGID_GROUP_LIST.tag()).ln(2);
+
+        return;
+    }
+
+    /**
+     * ジョイントリストを出力する。
+     * @param model モデルデータ
+     * @throws IOException 出力エラー
+     */
+    void putJointList(PmdModel model)
+            throws IOException{
+        ind().putSimpleSTag(PmdTag.JOINT_LIST.tag()).ln();
+
+        pushNest();
+        boolean dumped = false;
+        List<JointInfo> jointList = model.getJointList();
+        for(JointInfo joint : jointList){
+            if( ! dumped ) ln();
+            putJoint(joint);
+            dumped = true;
+        }
+        popNest();
+
+        ind().putETag(PmdTag.JOINT_LIST.tag()).ln(2);
+
+        return;
+    }
+
+    /**
+     * 個別のジョイント情報を出力する。
+     * @param joint ジョイント情報
+     * @throws IOException 出力エラー
+     */
+    private void putJoint(JointInfo joint)
+            throws IOException{
+        I18nText i18nName = joint.getJointName();
+
+        this.exp.putLocalNameComment(i18nName);
+        ln();
+        ind().putOpenSTag(PmdTag.JOINT.tag()).sp();
+        this.exp.putPrimaryNameAttr(PmdAttr.NAME, i18nName);
+        sp().putCloseSTag().ln();
+        pushNest();
+
+        this.exp.putI18nName(i18nName);
+
+        RigidInfo rigidA = joint.getRigidA();
+        RigidInfo rigidB = joint.getRigidB();
+
+        ind();
+        putLineComment(
+                "["
+                        + rigidA.getRigidName().getText()
+                + "]\u0020<=>\u0020["
+                        + rigidB.getRigidName().getText()
+                + "]");
+        ln();
+
+        ind().putOpenSTag(PmdTag.JOINTED_RIGID_PAIR.tag()).sp();
+        this.exp.putNumberedIdAttr(PmdAttr.RIGID_IDREF_1,
+                ExtraExporter.PFX_RIGID, rigidA);
+        sp();
+        this.exp.putNumberedIdAttr(PmdAttr.RIGID_IDREF_2,
+                ExtraExporter.PFX_RIGID, rigidB);
+        sp();
+        putCloseEmpty().ln(2);
+
+        putJointLimit(joint);
+        putJointElastic(joint);
+
+        popNest();
+        ind().putETag(PmdTag.JOINT.tag()).ln(2);
+
+        return;
+    }
+
+    /**
+     * ジョイント制限情報を出力する。
+     * @param joint ジョイント情報
+     * @throws IOException 出力エラー
+     */
+    private void putJointLimit(JointInfo joint)
+            throws IOException{
+        MkPos3D position = joint.getPosition();
+        ind();
+        this.exp.putPosition(position);
+        ln();
+
+        ind().putOpenSTag(PmdTag.LIMIT_POSITION.tag()).ln();
+        pushNest();
+        TripletRange posRange = joint.getPositionRange();
+        putTripletRangeAttr(posRange);
+        popNest();
+        ind().putCloseEmpty().ln(2);
+
+        Rad3d rotation = joint.getRotation();
+        ind();
+        this.exp.putRadRotation(rotation);
+        ln();
+
+        ind().putOpenSTag(PmdTag.LIMIT_ROTATION.tag()).ln();
+        pushNest();
+        TripletRange rotRange = joint.getRotationRange();
+        putTripletRangeAttr(rotRange);
+        popNest();
+        ind().putCloseEmpty().ln(2);
+
+        return;
+    }
+
+    /**
+     * XYZ範囲属性を出力する。
+     * @param range XYZ範囲
+     * @throws IOException 出力エラー
+     */
+    private void putTripletRangeAttr(TripletRange range)
+            throws IOException{
+        ind();
+        putFloatAttr(PmdAttr.X_FROM.attr(), range.getXFrom()).sp();
+        putFloatAttr(PmdAttr.X_TO.attr(),   range.getXTo()  ).ln();
+
+        ind();
+        putFloatAttr(PmdAttr.Y_FROM.attr(), range.getYFrom()).sp();
+        putFloatAttr(PmdAttr.Y_TO.attr(),   range.getYTo()  ).ln();
+
+        ind();
+        putFloatAttr(PmdAttr.Z_FROM.attr(), range.getZFrom()).sp();
+        putFloatAttr(PmdAttr.Z_TO.attr(),   range.getZTo()  ).ln();
+
+        return;
+    }
+
+    /**
+     * ジョイント弾性情報を出力する。
+     * @param joint ジョイント情報
+     * @throws IOException 出力エラー
+     */
+    private void putJointElastic(JointInfo joint)
+            throws IOException{
+        ind().putOpenSTag(PmdTag.ELASTIC_POSITION.tag()).sp();
+        MkPos3D elaPosition = joint.getElasticPosition();
+        putFloatAttr(PmdAttr.X.attr(),
+                (float) elaPosition.getXpos()).sp();
+        putFloatAttr(PmdAttr.Y.attr(),
+                (float) elaPosition.getYpos()).sp();
+        putFloatAttr(PmdAttr.Z.attr(),
+                (float) elaPosition.getZpos()).sp();
+        putCloseEmpty().ln();
+
+        ind().putOpenSTag(PmdTag.ELASTIC_ROTATION.tag()).sp();
+        Deg3d elaRotation = joint.getElasticRotation();
+        putFloatAttr(PmdAttr.X_DEG.attr(),
+                elaRotation.getXDeg()).sp();
+        putFloatAttr(PmdAttr.Y_DEG.attr(),
+                elaRotation.getYDeg()).sp();
+        putFloatAttr(PmdAttr.Z_DEG.attr(),
+                elaRotation.getZDeg()).sp();
+        putCloseEmpty().ln(2);
+
+        return;
+    }
+
+}
diff --git a/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/ExporterMaterial.java b/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/ExporterMaterial.java
new file mode 100644 (file)
index 0000000..92b759e
--- /dev/null
@@ -0,0 +1,258 @@
+/*
+ * material xml exporter
+ *
+ * License : The MIT License
+ * Copyright(c) 2013 MikuToga Partners
+ */
+
+package jp.sfjp.mikutoga.pmd.model.xml;
+
+import java.awt.Color;
+import java.io.IOException;
+import java.util.List;
+import jp.sfjp.mikutoga.corelib.I18nText;
+import jp.sfjp.mikutoga.pmd.model.Material;
+import jp.sfjp.mikutoga.pmd.model.PmdModel;
+import jp.sfjp.mikutoga.pmd.model.ShadeInfo;
+import jp.sfjp.mikutoga.pmd.model.ToonMap;
+import jp.sfjp.mikutoga.xml.ProxyXmlExporter;
+
+/**
+ * マテリアル設定のXMLエクスポーター。
+ */
+class ExporterMaterial extends ProxyXmlExporter {
+
+    // sRGBカラー情報配列インデックス
+    private static final int IDX_RED   = 0;
+    private static final int IDX_GREEN = 1;
+    private static final int IDX_BLUE  = 2;
+    private static final int IDX_ALPHA = 3;
+
+
+    private final ExtraExporter exp;
+
+
+    /**
+     * コンストラクタ。
+     * @param delegate 委譲先
+     */
+    ExporterMaterial(PmdXmlExporter delegate) {
+        super(delegate);
+        this.exp = new ExtraExporter(delegate);
+        return;
+    }
+
+    /**
+     * マテリアル素材一覧を出力する。
+     * @param model モデルデータ
+     * @throws IOException 出力エラー
+     */
+    void putMaterialList(PmdModel model)
+            throws IOException{
+        ind().putSimpleSTag(PmdTag.MATERIAL_LIST.tag()).ln();
+
+        pushNest();
+        int ct = 0;
+        boolean dumped = false;
+        List<Material> materialList = model.getMaterialList();
+        for(Material material : materialList){
+            if( ! dumped ) ln();
+            putMaterial(material, ct++);
+            dumped = true;
+        }
+        popNest();
+
+        ind().putETag(PmdTag.MATERIAL_LIST.tag()).ln(2);
+
+        return;
+    }
+
+    /**
+     * マテリアル素材情報を出力する。
+     * @param material マテリアル素材
+     * @param no マテリアル通し番号
+     * @throws IOException 出力エラー
+     */
+    private void putMaterial(Material material, int no)
+            throws IOException{
+        I18nText name = material.getMaterialName();
+        String primary = name.getPrimaryText();
+        String local = name.getText();
+        if(local != null && local.length() > 0){
+            ind().putLineComment(local).ln();
+        }
+
+        ind().putOpenSTag(PmdTag.MATERIAL.tag()).sp();
+        if(primary != null && primary.length() > 0){
+            putAttr(PmdAttr.NAME.attr(), primary).sp();
+        }
+        String bool;
+        if(material.getEdgeAppearance()) bool = "true";
+        else                             bool = "false";
+        putAttr(PmdAttr.SHOW_EDGE.attr(), bool);
+        sp();
+        this.exp.putNumberedIdAttr(PmdAttr.SURFACE_GROUP_IDREF,
+                          ExtraExporter.PFX_SURFACEGROUP, no);
+        sp().putCloseSTag().ln();
+        pushNest();
+
+        this.exp.putI18nName(name);
+
+        putPhongShade(material);
+
+        ShadeInfo shade = material.getShadeInfo();
+
+        if(shade.isValidToonIndex()){
+            putToon(shade);
+        }
+
+        putShadeFile(shade);
+
+        popNest();
+        ind().putETag(PmdTag.MATERIAL.tag()).ln(2);
+
+        return;
+    }
+
+    /**
+     * フォンシェーディング情報を出力する。
+     * @param material マテリアル
+     * @throws IOException 出力エラー
+     */
+    private void putPhongShade(Material material)
+            throws IOException{
+        float[] rgba = null;
+
+        ind().putOpenSTag(PmdTag.DIFFUSE.tag()).sp();
+        Color diffuse = material.getDiffuseColor();
+        rgba = diffuse.getRGBComponents(rgba);
+        putTriColor(rgba);
+        putFloatAttr(PmdAttr.ALPHA.attr(), rgba[IDX_ALPHA]).sp();
+        putCloseEmpty().ln();
+
+        float shininess = material.getShininess();
+        ind().putOpenSTag(PmdTag.SPECULAR.tag()).sp();
+        Color specular = material.getSpecularColor();
+        rgba = specular.getRGBComponents(rgba);
+        putTriColor(rgba);
+        putFloatAttr(PmdAttr.SHININESS.attr(), shininess).sp();
+        putCloseEmpty().ln();
+
+        ind().putOpenSTag(PmdTag.AMBIENT.tag()).sp();
+        Color ambient = material.getAmbientColor();
+        rgba = ambient.getRGBComponents(rgba);
+        putTriColor(rgba);
+        putCloseEmpty().ln();
+
+        return;
+    }
+
+    /**
+     * 三原色属性を出力する。
+     * @param rgb カラー情報
+     * @throws IOException 出力エラー
+     */
+    private void putTriColor(float[] rgb)
+            throws IOException{
+        putFloatAttr(PmdAttr.R.attr(), rgb[IDX_RED]  ).sp();
+        putFloatAttr(PmdAttr.G.attr(), rgb[IDX_GREEN]).sp();
+        putFloatAttr(PmdAttr.B.attr(), rgb[IDX_BLUE] ).sp();
+
+        return;
+    }
+
+    /**
+     * マテリアルのトゥーン情報を出力する。
+     * @param shade シェーディング情報
+     * @throws IOException 出力エラー
+     */
+    private void putToon(ShadeInfo shade)
+            throws IOException{
+        ind().putOpenSTag(PmdTag.TOON.tag()).sp();
+
+        int toonIdx = shade.getToonIndex();
+        this.exp.putNumberedIdAttr(PmdAttr.TOONFILE_IDREF,
+                          ExtraExporter.PFX_TOONFILE, toonIdx);
+        sp();
+
+        putCloseEmpty();
+
+        String toonFileName = shade.getToonFileName();
+        if(toonFileName != null && toonFileName.length() > 0){
+            sp().putLineComment(toonFileName);
+        }
+        ln();
+
+        return;
+    }
+
+    /**
+     * シェーディングファイル情報を出力する。
+     * @param shade シェーディング情報
+     * @throws IOException 出力エラー
+     */
+    private void putShadeFile(ShadeInfo shade)
+            throws IOException{
+        String textureFileName   = shade.getTextureFileName();
+        String spheremapFileName = shade.getSpheremapFileName();
+
+        if(textureFileName != null && textureFileName.length() > 0){
+            ind().putOpenSTag(PmdTag.TEXTURE_FILE.tag()).sp();
+            putAttr(PmdAttr.WINFILE_NAME.attr(),
+                    textureFileName).sp();
+            putCloseEmpty().ln();
+        }
+
+        if(spheremapFileName != null && spheremapFileName.length() > 0){
+            ind().putOpenSTag(PmdTag.SPHEREMAP_FILE.tag()).sp();
+            putAttr(PmdAttr.WINFILE_NAME.attr(),
+                    spheremapFileName).sp();
+            putCloseEmpty().ln();
+        }
+
+        return;
+    }
+
+    /**
+     * トゥーンファイルマッピング情報を出力する。
+     * @param model モデルデータ
+     * @throws IOException 出力エラー
+     */
+    void putToonMap(PmdModel model)
+            throws IOException{
+        ind().putSimpleSTag(PmdTag.TOON_MAP.tag()).ln();
+        pushNest();
+
+        ToonMap map = model.getToonMap();
+        for(int index = 0; index < ToonMap.MAX_CUSTOM_TOON; index++){
+            ind();
+            putToonDef(map, index);
+            ln();
+        }
+
+        popNest();
+        ind().putETag(PmdTag.TOON_MAP.tag()).ln(2);
+        return;
+    }
+
+    /**
+     * 個別のトゥーンファイル情報を出力する。
+     * @param map トゥーンマップ
+     * @param index インデックス値
+     * @throws IOException 出力エラー
+     */
+    private void putToonDef(ToonMap map, int index)
+            throws IOException{
+        putOpenSTag(PmdTag.TOON_DEF.tag()).sp();
+        this.exp.putNumberedIdAttr(PmdAttr.TOONFILE_ID,
+                ExtraExporter.PFX_TOONFILE, index);
+        sp();
+        putIntAttr(PmdAttr.INDEX.attr(), index).sp();
+        String toonFile = map.getIndexedToon(index);
+        putAttr(PmdAttr.WINFILE_NAME.attr(), toonFile).sp();
+        putCloseEmpty();
+        this.exp.putUnescapedComment(toonFile);
+        return;
+    }
+
+}
diff --git a/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/ExporterMorph.java b/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/ExporterMorph.java
new file mode 100644 (file)
index 0000000..87dcfc6
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * morph xml exporter
+ *
+ * License : The MIT License
+ * Copyright(c) 2013 MikuToga Partners
+ */
+
+package jp.sfjp.mikutoga.pmd.model.xml;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import jp.sfjp.mikutoga.corelib.I18nText;
+import jp.sfjp.mikutoga.math.MkPos3D;
+import jp.sfjp.mikutoga.pmd.MorphType;
+import jp.sfjp.mikutoga.pmd.model.MorphPart;
+import jp.sfjp.mikutoga.pmd.model.MorphVertex;
+import jp.sfjp.mikutoga.pmd.model.PmdModel;
+import jp.sfjp.mikutoga.pmd.model.Vertex;
+import jp.sfjp.mikutoga.xml.ProxyXmlExporter;
+
+/**
+ * モーフ定義のXMLエクスポーター。
+ */
+class ExporterMorph extends ProxyXmlExporter {
+
+    private static final String MORPHTYPE_COMMENT =
+          "Morph types:\n"
+        + "[1 : EYEBROW : まゆ   ]\n"
+        + "[2 : EYE     : 目     ]\n"
+        + "[3 : LIP     : リップ ]\n"
+        + "[4 : EXTRA   : その他 ]\n";
+
+
+    private final ExtraExporter exp;
+
+
+    /**
+     * コンストラクタ。
+     * @param delegate 委譲先
+     */
+    ExporterMorph(PmdXmlExporter delegate) {
+        super(delegate);
+        this.exp = new ExtraExporter(delegate);
+        return;
+    }
+
+    /**
+     * モーフ定義データを出力する。
+     * @param model モデルデータ
+     * @throws IOException 出力エラー
+     */
+    void putMorphList(PmdModel model) throws IOException{
+        ind().putSimpleSTag(PmdTag.MORPH_LIST.tag()).ln();
+        pushNest();
+
+        boolean dumped = false;
+        Map<MorphType, List<MorphPart>> morphMap = model.getMorphMap();
+        for(MorphType type : MorphType.values()){
+            if(type == MorphType.BASE) continue;
+            List<MorphPart> partList = morphMap.get(type);
+            if(partList == null) continue;
+            for(MorphPart part : partList){
+                if( ! dumped ){
+                    ln().putBlockComment(MORPHTYPE_COMMENT).ln();
+                }
+                putMorphPart(part);
+                dumped = true;
+            }
+        }
+
+        popNest();
+        ind().putETag(PmdTag.MORPH_LIST.tag()).ln(2);
+
+        return;
+    }
+
+    /**
+     * 個別のモーフ情報を出力する。
+     * @param part モーフ情報
+     * @throws IOException 出力エラー
+     */
+    private void putMorphPart(MorphPart part) throws IOException{
+        I18nText i18nName = part.getMorphName();
+        String primary = i18nName.getPrimaryText();
+
+        this.exp.putLocalNameComment(i18nName);
+        ln();
+        ind().putOpenSTag(PmdTag.MORPH.tag()).sp();
+        putAttr(PmdAttr.NAME.attr(), primary).sp();
+        putAttr(PmdAttr.TYPE.attr(), part.getMorphType().name());
+        sp().putCloseSTag();
+        ln();
+        pushNest();
+
+        this.exp.putI18nName(i18nName);
+
+        for(MorphVertex mvertex : part){
+            MkPos3D offset = mvertex.getOffset();
+            Vertex base = mvertex.getBaseVertex();
+
+            ind().putOpenSTag(PmdTag.MORPH_VERTEX.tag()).sp();
+            this.exp.putNumberedIdAttr(PmdAttr.VERTEX_IDREF,
+                    ExtraExporter.PFX_VERTEX, base);
+            sp();
+            putFloatAttr(PmdAttr.XOFF.attr(),
+                    (float) offset.getXpos()).sp();
+            putFloatAttr(PmdAttr.YOFF.attr(),
+                    (float) offset.getYpos()).sp();
+            putFloatAttr(PmdAttr.ZOFF.attr(),
+                    (float) offset.getZpos()).sp();
+            putCloseEmpty();
+            ln();
+        }
+
+        popNest();
+        ind().putETag(PmdTag.MORPH.tag()).ln(2);
+
+        return;
+    }
+
+}
diff --git a/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/ExporterShape.java b/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/ExporterShape.java
new file mode 100644 (file)
index 0000000..ce14356
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * shape xml exporter
+ *
+ * License : The MIT License
+ * Copyright(c) 2013 MikuToga Partners
+ */
+
+package jp.sfjp.mikutoga.pmd.model.xml;
+
+import java.io.IOException;
+import java.util.List;
+import jp.sfjp.mikutoga.math.MkPos2D;
+import jp.sfjp.mikutoga.math.MkPos3D;
+import jp.sfjp.mikutoga.math.MkVec3D;
+import jp.sfjp.mikutoga.pmd.model.BoneInfo;
+import jp.sfjp.mikutoga.pmd.model.Material;
+import jp.sfjp.mikutoga.pmd.model.PmdModel;
+import jp.sfjp.mikutoga.pmd.model.Surface;
+import jp.sfjp.mikutoga.pmd.model.Vertex;
+import jp.sfjp.mikutoga.xml.ProxyXmlExporter;
+
+/**
+ * 形状設定のXMLエクスポーター。
+ */
+class ExporterShape extends ProxyXmlExporter {
+
+    private final ExtraExporter exp;
+
+
+    /**
+     * コンストラクタ。
+     * @param delegate 委譲先
+     */
+    ExporterShape(PmdXmlExporter delegate) {
+        super(delegate);
+        this.exp = new ExtraExporter(delegate);
+        return;
+    }
+
+    /**
+     * サーフェイスグループリストを出力する。
+     * @param model モデルデータ
+     * @throws IOException 出力エラー
+     */
+    void putSurfaceGroupList(PmdModel model)
+            throws IOException{
+        ind().putSimpleSTag(PmdTag.SURFACE_GROUP_LIST.tag()).ln();
+
+        pushNest();
+        int ct = 0;
+        boolean dumped = false;
+        List<Material> materialList = model.getMaterialList();
+        for(Material material : materialList){
+            List<Surface> surfaceList = material.getSurfaceList();
+            if( ! dumped ) ln();
+            putSurfaceList(surfaceList, ct++);
+            dumped = true;
+        }
+        popNest();
+
+        ind().putETag(PmdTag.SURFACE_GROUP_LIST.tag()).ln(2);
+
+        return;
+    }
+
+    /**
+     * 個別のサーフェイスグループを出力する。
+     * @param surfaceList サーフェイスのリスト
+     * @param index グループインデックス
+     * @throws IOException 出力エラー
+     */
+    private void putSurfaceList(List<Surface> surfaceList,
+                                              int index)
+            throws IOException{
+        ind().putOpenSTag(PmdTag.SURFACE_GROUP.tag()).sp();
+        this.exp.putNumberedIdAttr(PmdAttr.SURFACE_GROUP_ID,
+                          ExtraExporter.PFX_SURFACEGROUP, index);
+        sp().putCloseSTag().ln();
+        pushNest();
+
+        for(Surface surface : surfaceList){
+            putSurface(surface);
+        }
+
+        popNest();
+        ind().putETag(PmdTag.SURFACE_GROUP.tag()).ln(2);
+
+        return;
+    }
+
+    /**
+     * 個別のサーフェイスを出力する。
+     * @param surface サーフェイス
+     * @throws IOException 出力エラー
+     */
+    private void putSurface(Surface surface)
+            throws IOException{
+        ind().putOpenSTag(PmdTag.SURFACE.tag()).sp();
+
+        Vertex vertex1 = surface.getVertex1();
+        Vertex vertex2 = surface.getVertex2();
+        Vertex vertex3 = surface.getVertex3();
+
+        this.exp.putNumberedIdAttr(PmdAttr.VERTEX_IDREF_1,
+                          ExtraExporter.PFX_VERTEX, vertex1);
+        sp();
+        this.exp.putNumberedIdAttr(PmdAttr.VERTEX_IDREF_2,
+                          ExtraExporter.PFX_VERTEX, vertex2);
+        sp();
+        this.exp.putNumberedIdAttr(PmdAttr.VERTEX_IDREF_3,
+                          ExtraExporter.PFX_VERTEX, vertex3);
+        sp();
+
+        putCloseEmpty().ln();
+        return;
+    }
+
+    /**
+     * 頂点リストを出力する。
+     * @param model モデルデータ
+     * @throws IOException 出力エラー
+     */
+    void putVertexList(PmdModel model)
+            throws IOException{
+        ind().putSimpleSTag(PmdTag.VERTEX_LIST.tag()).ln();
+
+        pushNest();
+        boolean dumped = false;
+        List<Vertex> vertexList = model.getVertexList();
+        for(Vertex vertex : vertexList){
+            if( ! dumped ) ln();
+            putVertex(vertex);
+            dumped = true;
+        }
+        popNest();
+
+        ind().putETag(PmdTag.VERTEX_LIST.tag()).ln(2);
+
+        return;
+    }
+
+    /**
+     * 個別の頂点情報を出力する。
+     * @param vertex 頂点
+     * @throws IOException 出力エラー
+     */
+    private void putVertex(Vertex vertex)
+            throws IOException{
+        String bool;
+        if(vertex.getEdgeAppearance()) bool = "true";
+        else                           bool = "false";
+
+        ind().putOpenSTag(PmdTag.VERTEX.tag()).sp();
+        this.exp.putNumberedIdAttr(PmdAttr.VERTEX_ID,
+                ExtraExporter.PFX_VERTEX, vertex);
+        sp();
+        putAttr(PmdAttr.SHOW_EDGE.attr(), bool);
+        sp().putCloseSTag().ln();
+        pushNest();
+
+        putVertexBody(vertex);
+
+        popNest();
+        ind().putETag(PmdTag.VERTEX.tag()).ln(2);
+
+        return;
+    }
+
+    /**
+     * 頂点情報の詳細を出力する。
+     * @param vertex 頂点
+     * @throws IOException 出力エラー
+     */
+    private void putVertexBody(Vertex vertex)
+            throws IOException{
+        MkPos3D position = vertex.getPosition();
+        ind();
+        this.exp.putPosition(position);
+        ln();
+
+        MkVec3D normal = vertex.getNormal();
+        ind().putOpenSTag(PmdTag.NORMAL.tag()).sp();
+        putFloatAttr(PmdAttr.X.attr(), (float) normal.getXVal())
+                .sp();
+        putFloatAttr(PmdAttr.Y.attr(), (float) normal.getYVal())
+                .sp();
+        putFloatAttr(PmdAttr.Z.attr(), (float) normal.getZVal())
+                .sp();
+        putCloseEmpty().ln();
+
+        MkPos2D uvPos = vertex.getUVPosition();
+        ind().putOpenSTag(PmdTag.UV_MAP.tag()).sp();
+        putFloatAttr(PmdAttr.U.attr(), (float) uvPos.getXpos()).sp();
+        putFloatAttr(PmdAttr.V.attr(), (float) uvPos.getYpos()).sp();
+        putCloseEmpty().ln();
+
+        BoneInfo boneA = vertex.getBoneA();
+        BoneInfo boneB = vertex.getBoneB();
+        int weight = vertex.getWeightA();
+        ind().putOpenSTag(PmdTag.SKINNING.tag()).sp();
+        this.exp.putNumberedIdAttr(PmdAttr.BONE_IDREF_1,
+                ExtraExporter.PFX_BONE, boneA);
+        sp();
+        this.exp.putNumberedIdAttr(PmdAttr.BONE_IDREF_2,
+                ExtraExporter.PFX_BONE, boneB);
+        sp();
+        putIntAttr(PmdAttr.WEIGHT_BALANCE.attr(), weight).sp();
+        putCloseEmpty().ln();
+
+        return;
+    }
+
+}
diff --git a/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/ExtraExporter.java b/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/ExtraExporter.java
new file mode 100644 (file)
index 0000000..f276698
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * extra xml exporter
+ *
+ * License : The MIT License
+ * Copyright(c) 2013 MikuToga Partners
+ */
+
+package jp.sfjp.mikutoga.pmd.model.xml;
+
+import java.io.IOException;
+import jp.sfjp.mikutoga.corelib.I18nText;
+import jp.sfjp.mikutoga.math.MkPos3D;
+import jp.sfjp.mikutoga.pmd.Rad3d;
+import jp.sfjp.mikutoga.pmd.model.SerialNumbered;
+import jp.sfjp.mikutoga.xml.ProxyXmlExporter;
+import jp.sfjp.mikutoga.xml.XmlExporter;
+
+/**
+ * XML出力機構の共通部。
+ */
+class ExtraExporter extends ProxyXmlExporter {
+
+    static final String PFX_SURFACEGROUP = "sg";
+    static final String PFX_TOONFILE     = "tf";
+    static final String PFX_VERTEX       = "vtx";
+    static final String PFX_BONE         = "bn";
+    static final String PFX_RIGID        = "rd";
+    static final String PFX_RIGIDGROUP   = "rg";
+
+    private static final char CAP_BASIC_LATIN = '\u007f';
+
+
+    /**
+     * コンストラクタ。
+     * @param delegate 委譲先
+     */
+    ExtraExporter(XmlExporter delegate){
+        super(delegate);
+        return;
+    }
+
+
+    /**
+     * 任意の文字列がBasicLatin文字のみから構成されるか判定する。
+     * @param seq 文字列
+     * @return null、長さ0もしくはBasicLatin文字のみから構成されるならtrue
+     */
+    static boolean hasOnlyBasicLatin(CharSequence seq){
+        if(seq == null) return true;
+        int length = seq.length();
+        for(int pos = 0; pos < length; pos++){
+            char ch = seq.charAt(pos);
+            if(ch > CAP_BASIC_LATIN) return false;
+        }
+        return true;
+    }
+
+
+    /**
+     * 文字参照によるエスケープを補佐するためのコメントを出力する。
+     * @param seq 文字列
+     * @throws IOException 出力エラー
+     */
+    void putUnescapedComment(CharSequence seq)
+            throws IOException{
+        if( ! isBasicLatinOnlyOut() ) return;
+        if(hasOnlyBasicLatin(seq)) return;
+        sp().putLineComment(seq);
+        return;
+    }
+
+    /**
+     * 多言語識別名属性のローカルな名前をコメント出力する。
+     * @param name 多言語識別名
+     * @throws IOException 出力エラー
+     */
+    void putLocalNameComment(I18nText name)
+            throws IOException{
+        String localName = name.getText();
+        if(localName.isEmpty()){
+            localName = "[NAMELESS]";
+        }
+        ind().putLineComment(localName);
+        return;
+    }
+
+    /**
+     * 多言語識別名属性のプライマリな名前を出力する。
+     * @param attr 属性名
+     * @param name 多言語識別名
+     * @throws IOException 出力エラー
+     */
+    void putPrimaryNameAttr(PmdAttr attr, I18nText name)
+            throws IOException{
+        String attrName = attr.attr();
+        String primaryName = name.getPrimaryText();
+        putAttr(attrName, primaryName);
+        return;
+    }
+
+    /**
+     * 多言語化された各種識別名を出力する。
+     * プライマリ名は出力対象外。
+     * @param text 多言語文字列
+     * @throws IOException 出力エラー
+     */
+    void putI18nName(I18nText text) throws IOException{
+        for(String lang639 : text.lang639CodeList()){
+            if(lang639.equals(I18nText.CODE639_PRIMARY)) continue;
+            String name = text.getI18nText(lang639);
+            ind().putOpenSTag(PmdTag.I18N_NAME.tag()).sp();
+            putAttr(PmdAttr.LANG.attr(), lang639).sp();
+            putAttr(PmdAttr.NAME.attr(), name).sp();
+            putCloseEmpty();
+            putUnescapedComment(name);
+            ln();
+        }
+        return;
+    }
+
+    /**
+     * 番号付けされたID(IDREF)属性を出力する。
+     * @param attr 属性名
+     * @param prefix IDプレフィクス
+     * @param num 番号
+     * @throws IOException 出力エラー
+     */
+    void putNumberedIdAttr(PmdAttr attr,
+                             CharSequence prefix,
+                             int num )
+            throws IOException{
+        String attrName = attr.attr();
+        putRawText(attrName).putRawCh('=');
+        putRawCh('"');
+        putRawText(prefix).putXsdInt(num);
+        putRawCh('"');
+        return;
+    }
+
+    /**
+     * 番号付けされたID(IDREF)属性を出力する。
+     * @param attr 属性名
+     * @param prefix IDプレフィクス
+     * @param numbered 番号付けされたオブジェクト
+     * @throws IOException 出力エラー
+     */
+    void putNumberedIdAttr(PmdAttr attr,
+                             CharSequence prefix,
+                             SerialNumbered numbered )
+            throws IOException{
+        putNumberedIdAttr(attr, prefix, numbered.getSerialNumber());
+        return;
+    }
+
+    /**
+     * 位置情報を出力する。
+     * @param position 位置情報
+     * @throws IOException 出力エラー
+     */
+    void putPosition(MkPos3D position)
+            throws IOException{
+        putOpenSTag("position").sp();
+
+        putFloatAttr(PmdAttr.X.attr(),
+                (float) position.getXpos()).sp();
+        putFloatAttr(PmdAttr.Y.attr(),
+                (float) position.getYpos()).sp();
+        putFloatAttr(PmdAttr.Z.attr(),
+                (float) position.getZpos()).sp();
+
+        putCloseEmpty();
+
+        return;
+    }
+
+    /**
+     * 姿勢情報(ラジアン)を出力する。
+     * @param rotation 姿勢情報
+     * @throws IOException 出力エラー
+     */
+    void putRadRotation(Rad3d rotation)
+            throws IOException{
+        putOpenSTag("radRotation").sp();
+
+        putFloatAttr(PmdAttr.X_RAD.attr(), rotation.getXRad()).sp();
+        putFloatAttr(PmdAttr.Y_RAD.attr(), rotation.getYRad()).sp();
+        putFloatAttr(PmdAttr.Z_RAD.attr(), rotation.getZRad()).sp();
+
+        putCloseEmpty();
+
+        return;
+    }
+
+}
diff --git a/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/OpenXmlMark.java b/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/OpenXmlMark.java
new file mode 100644 (file)
index 0000000..d5d9a7b
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * annotation for beginning XML-element
+ *
+ * License : The MIT License
+ * Copyright(c) 2013 MikuToga Partners
+ */
+
+package jp.sfjp.mikutoga.pmd.model.xml;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * XML要素開始通知用注釈。
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@interface OpenXmlMark {
+    /** タグ指定。 */
+    PmdTag value();
+}
diff --git a/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/PmdAttr.java b/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/PmdAttr.java
new file mode 100644 (file)
index 0000000..1091ef0
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * attributes of pmd xml file
+ *
+ * License : The MIT License
+ * Copyright(c) 2013 MikuToga Partners
+ */
+
+package jp.sfjp.mikutoga.pmd.model.xml;
+
+/**
+ * XML属性名一覧。
+ * @see "http://mikutoga.sourceforge.jp/xml/xsd/pmdxml-101009.xsd"
+ * @see "http://mikutoga.sourceforge.jp/xml/xsd/pmdxml-130128.xsd"
+ *
+ */
+enum PmdAttr {
+
+    VERSION             ("version"),
+    SCHEMA_VERSION      ("schemaVersion"),
+
+    NAME                ("name"),
+    LANG                ("lang"),
+
+    CONTENT             ("content"),
+
+    SURFACE_GROUP_IDREF ("surfaceGroupIdRef"),
+    R                   ("r"),
+    G                   ("g"),
+    B                   ("b"),
+    ALPHA               ("alpha"),
+    SHININESS           ("shininess"),
+    TOONFILE_ID         ("toonFileId"),
+    TOONFILE_IDREF      ("toonFileIdRef"),
+    WINFILE_NAME        ("winFileName"),
+    INDEX               ("index"),
+
+    BONE_ID             ("boneId"),
+    TYPE                ("type"),
+    X                   ("x"),
+    Y                   ("y"),
+    Z                   ("z"),
+    PREV_BONE_IDREF     ("prevBoneIdRef"),
+    NEXT_BONE_IDREF     ("nextBoneIdRef"),
+    BONE_IDREF          ("boneIdRef"),
+    RATIO               ("ratio"),
+
+    IK_BONE_IDREF       ("ikBoneIdRef"),
+    RECURSIVE_DEPTH     ("recursiveDepth"),
+    WEIGHT              ("weight"),
+
+    VERTEX_IDREF        ("vtxIdRef"),
+    XOFF                ("xOff"),
+    YOFF                ("yOff"),
+    ZOFF                ("zOff"),
+
+    RIGID_ID            ("rigidId"),
+    RIGID_IDREF         ("rigidIdRef"),
+    BEHAVIOR            ("behavior"),
+    RADIUS              ("radius"),
+    WIDTH               ("width"),
+    HEIGHT              ("height"),
+    DEPTH               ("depth"),
+    X_RAD               ("xRad"),
+    Y_RAD               ("yRad"),
+    Z_RAD               ("zRad"),
+
+    MASS                ("mass"),
+    DAMPING_POSITION    ("dampingPosition"),
+    DAMPING_ROTATION    ("dampingRotation"),
+    RESTITUTION         ("restitution"),
+    FRICTION            ("friction"),
+
+    RIGID_GROUP_ID      ("rigidGroupId"),
+    RIGID_GROUP_IDREF   ("rigidGroupIdRef"),
+
+    RIGID_IDREF_1       ("rigidIdRef1"),
+    RIGID_IDREF_2       ("rigidIdRef2"),
+    X_FROM              ("xFrom"),
+    X_TO                ("xTo"),
+    Y_FROM              ("yFrom"),
+    Y_TO                ("yTo"),
+    Z_FROM              ("zFrom"),
+    Z_TO                ("zTo"),
+    X_DEG               ("xDeg"),
+    Y_DEG               ("yDeg"),
+    Z_DEG               ("zDeg"),
+
+    SURFACE_GROUP_ID    ("surfaceGroupId"),
+    VERTEX_IDREF_1      ("vtxIdRef1"),
+    VERTEX_IDREF_2      ("vtxIdRef2"),
+    VERTEX_IDREF_3      ("vtxIdRef3"),
+
+    VERTEX_ID           ("vtxId"),
+    SHOW_EDGE           ("showEdge"),
+    U                   ("u"),
+    V                   ("v"),
+    BONE_IDREF_1        ("boneIdRef1"),
+    BONE_IDREF_2        ("boneIdRef2"),
+    WEIGHT_BALANCE      ("weightBalance"),
+
+    ;
+
+    private final String attrName;
+
+
+    /**
+     * コンストラクタ。
+     * @param attrName 属性名
+     */
+    private PmdAttr(String attrName){
+        this.attrName = attrName.intern();
+        return;
+    }
+
+    /**
+     * 属性名を返す。
+     * @return 属性名
+     */
+    String attr(){
+        return this.attrName;
+    }
+
+}
diff --git a/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/PmdTag.java b/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/PmdTag.java
new file mode 100644 (file)
index 0000000..d7a977f
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * tags of pmd xml file
+ *
+ * License : The MIT License
+ * Copyright(c) 2013 MikuToga Partners
+ */
+
+package jp.sfjp.mikutoga.pmd.model.xml;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * XML要素名一覧。
+ * @see "http://mikutoga.sourceforge.jp/xml/xsd/pmdxml-101009.xsd"
+ * @see "http://mikutoga.sourceforge.jp/xml/xsd/pmdxml-130128.xsd"
+ */
+enum PmdTag {
+
+    PMD_MODEL           ("pmdModel"),
+
+    BR                  ("br"),
+    I18N_NAME           ("i18nName"),
+    DESCRIPTION         ("description"),
+    LICENSE             ("license"),
+    CREDITS             ("credits"),
+    META                ("meta"),
+
+    MATERIAL_LIST       ("materialList"),
+    MATERIAL            ("material"),
+    DIFFUSE             ("diffuse"),
+    SPECULAR            ("specular"),
+    AMBIENT             ("ambient"),
+    TOON                ("toon"),
+    TEXTURE_FILE        ("textureFile"),
+    SPHEREMAP_FILE      ("spheremapFile"),
+
+    TOON_MAP            ("toonMap"),
+    TOON_DEF            ("toonDef"),
+
+    BONE_LIST           ("boneList"),
+    BONE                ("bone"),
+    POSITION            ("position"),
+    IK_BONE             ("ikBone"),
+    SOURCE_BONE         ("sourceBone"),
+    ROTATION_RATIO      ("rotationRatio"),
+    BONE_CHAIN          ("boneChain"),
+
+    BONE_GROUP_LIST     ("boneGroupList"),
+    BONE_GROUP          ("boneGroup"),
+    BONE_GROUP_MEMBER   ("boneGroupMember"),
+
+    IK_CHAIN_LIST       ("ikChainList"),
+    IK_CHAIN            ("ikChain"),
+    CHAIN_ORDER         ("chainOrder"),
+
+    MORPH_LIST          ("morphList"),
+    MORPH               ("morph"),
+    MORPH_VERTEX        ("morphVertex"),
+
+    RIGID_LIST          ("rigidList"),
+    RIGID               ("rigid"),
+    LINKED_BONE         ("linkedBone"),
+    RIGID_SHAPE_SPHERE  ("rigidShapeSphere"),
+    RIGID_SHAPE_BOX     ("rigidShapeBox"),
+    RIGID_SHAPE_CAPSULE ("rigidShapeCapsule"),
+    RAD_ROTATION        ("radRotation"),
+    DYNAMICS            ("dynamics"),
+    THROUGH_RIGID_GROUP ("throughRigidGroup"),
+
+    RIGID_GROUP_LIST    ("rigidGroupList"),
+    RIGID_GROUP         ("rigidGroup"),
+    RIGID_GROUP_MEMBER  ("rigidGroupMember"),
+
+    JOINT_LIST          ("jointList"),
+    JOINT               ("joint"),
+    JOINTED_RIGID_PAIR  ("jointedRigidPair"),
+    LIMIT_POSITION      ("limitPosition"),
+    LIMIT_ROTATION      ("limitRotation"),
+    ELASTIC_POSITION    ("elasticPosition"),
+    ELASTIC_ROTATION    ("elasticRotation"),
+
+    SURFACE_GROUP_LIST  ("surfaceGroupList"),
+    SURFACE_GROUP       ("surfaceGroup"),
+    SURFACE             ("surface"),
+
+    VERTEX_LIST         ("vertexList"),
+    VERTEX              ("vertex"),
+    NORMAL              ("normal"),
+    UV_MAP              ("uvMap"),
+    SKINNING            ("skinning"),
+
+    ;
+
+    private static final Map<String, PmdTag> NAME_MAP =
+            new HashMap<String, PmdTag>();
+
+    static{
+        for(PmdTag tag : values()){
+            NAME_MAP.put(tag.tag(), tag);
+        }
+    }
+
+
+    private final String tagName;
+
+
+    /**
+     * コンストラクタ。
+     * @param tagName 要素名
+     */
+    private PmdTag(String tagName){
+        this.tagName = tagName.intern();
+        return;
+    }
+
+
+    /**
+     * XML要素名から列挙子を得る。
+     * @param name 要素名
+     * @return 列挙子。合致する物がなければnull。
+     */
+    static PmdTag parse(String name){
+        PmdTag result;
+        result = NAME_MAP.get(name);
+        return result;
+    }
+
+
+    /**
+     * XML要素名を返す。
+     * @return 要素名
+     */
+    String tag(){
+        return this.tagName;
+    }
+
+}
diff --git a/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/PmdXmlExporter.java b/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/PmdXmlExporter.java
new file mode 100644 (file)
index 0000000..01f5a8a
--- /dev/null
@@ -0,0 +1,366 @@
+/*
+ * pmd-xml exporter
+ *
+ * License : The MIT License
+ * Copyright(c) 2010 MikuToga Partners
+ */
+
+package jp.sfjp.mikutoga.pmd.model.xml;
+
+import java.io.IOException;
+import jp.sfjp.mikutoga.corelib.I18nText;
+import jp.sfjp.mikutoga.pmd.model.PmdModel;
+import jp.sfjp.mikutoga.xml.BasicXmlExporter;
+import jp.sfjp.mikutoga.xml.XmlResourceResolver;
+
+/**
+ * PMDモーションデータをXMLへエクスポートする。
+ */
+public class PmdXmlExporter extends BasicXmlExporter{
+
+    private static final String XML_VER = "1.0";
+    private static final String XML_ENC = "UTF-8";
+    private static final String XML_DECL =
+              "<?xml version=\"" + XML_VER
+            + "\" encoding=\"" + XML_ENC
+            + "\" ?>";
+
+    private static final String XSINS = "xsi";
+
+    private static final String TOP_COMMENT =
+              "\u0020\u0020"               + "MikuMikuDance\n"
+            + "\u0020\u0020\u0020\u0020" + "model-data(*.pmd) on XML";
+
+    /** 改行文字列 CR。 */
+    private static final String CR = "\r";       // 0x0d
+    /** 改行文字列 LF。 */
+    private static final String LF = "\n";       // 0x0a
+    /** 改行文字列 CRLF。 */
+    private static final String CRLF = CR + LF;  // 0x0d, 0x0a
+
+
+    private XmlModelFileType xmlType = XmlModelFileType.XML_130128;
+
+    private String generator = null;
+
+    private final ExporterMaterial materialExporter;
+    private final ExporterBone     boneExporter;
+    private final ExporterMorph    morphExporter;
+    private final ExporterDynamics dynamicsExporter;
+    private final ExporterShape    shapeExporter;
+
+    private final ExtraExporter exp;
+
+
+    /**
+     * コンストラクタ。
+     */
+    public PmdXmlExporter(){
+        super();
+
+        this.materialExporter = new ExporterMaterial(this);
+        this.boneExporter     = new ExporterBone(this);
+        this.morphExporter    = new ExporterMorph(this);
+        this.dynamicsExporter = new ExporterDynamics(this);
+        this.shapeExporter    = new ExporterShape(this);
+
+        this.exp = new ExtraExporter(this);
+
+        this.boneExporter.setXmlFileType(this.xmlType);
+
+        return;
+    }
+
+
+    /**
+     * 出力XMLファイル種別を返す。
+     * @return ファイル種別
+     */
+    public XmlModelFileType getXmlFileType(){
+        return this.xmlType;
+    }
+
+    /**
+     * 出力XMLファイル種別を設定する。
+     * @param type ファイル種別
+     */
+    public void setXmlFileType(XmlModelFileType type){
+        switch(type){
+        case XML_101009:
+        case XML_130128:
+            this.xmlType = type;
+            break;
+        case XML_AUTO:
+            this.xmlType = XmlModelFileType.XML_130128;
+            break;
+        default:
+            throw new IllegalArgumentException();
+        }
+
+        assert this.xmlType == XmlModelFileType.XML_101009
+            || this.xmlType == XmlModelFileType.XML_130128;
+
+        this.boneExporter.setXmlFileType(this.xmlType);
+
+        return;
+    }
+
+    /**
+     * Generatorメタ情報を設定する。
+     * @param generatorArg Generatorメタ情報。表示したくないときはnull
+     */
+    public void setGenerator(String generatorArg){
+        this.generator = generatorArg;
+        return;
+    }
+
+    /**
+     * Generatorメタ情報を返す。
+     * @return Generatorメタ情報。表示したくないときはnull
+     */
+    public String getGenerator(){
+        return this.generator;
+    }
+
+    /**
+     * PMDモデルデータをXML形式で出力する。
+     * @param model PMDモデルデータ
+     * @param xmlOut XML出力先
+     * @throws IOException 出力エラー
+     */
+    public void putPmdXml(PmdModel model, Appendable xmlOut)
+            throws IOException{
+        setAppendable(xmlOut);
+
+        try{
+            putPmdXmlImpl(model);
+        }finally{
+            flush();
+        }
+
+        return;
+    }
+
+    /**
+     * PMDモデルデータをXML形式で出力する。
+     * @param model PMDモデルデータ
+     * @throws IOException 出力エラー
+     */
+    private void putPmdXmlImpl(PmdModel model) throws IOException{
+        putPmdRootOpen(model);
+
+        putModelInfo(model);
+        putMetaInfo();
+
+        this.materialExporter.putMaterialList(model);
+        this.materialExporter.putToonMap(model);
+
+        this.boneExporter.putBoneList(model);
+        this.boneExporter.putBoneGroupList(model);
+        this.boneExporter.putIKChainList(model);
+
+        this.morphExporter.putMorphList(model);
+
+        this.dynamicsExporter.putRigidList(model);
+        this.dynamicsExporter.putRigidGroupList(model);
+        this.dynamicsExporter.putJointList(model);
+
+        this.shapeExporter.putSurfaceGroupList(model);
+        this.shapeExporter.putVertexList(model);
+
+        ind().putETag(PmdTag.PMD_MODEL.tag()).ln(2);
+        ind().putLineComment("EOF").ln();
+
+        return;
+    }
+
+    /**
+     * ルートタグ開始を出力する。
+     * @param model モデル情報
+     * @throws IOException 出力エラー
+     */
+    private void putPmdRootOpen(PmdModel model)
+            throws IOException{
+        ind().putRawText(XML_DECL).ln(2);
+        ind().putBlockComment(TOP_COMMENT).ln(2);
+
+        I18nText modelName = model.getModelName();
+        ind();
+        this.exp.putLocalNameComment(modelName);
+        ln();
+
+        ind().putOpenSTag(PmdTag.PMD_MODEL.tag()).ln();
+        pushNest();
+
+        putPmdRootAttr(model);
+
+        popNest();
+        putCloseSTag().ln(2);
+
+        return;
+    }
+
+    /**
+     * ルートタグ属性を出力する。
+     * @param model モデル情報
+     * @throws IOException 出力エラー
+     */
+    private void putPmdRootAttr(PmdModel model)
+            throws IOException{
+        String namespace;
+        String schemaUrl;
+        String schemaVer;
+
+        if(this.xmlType == XmlModelFileType.XML_101009){
+            namespace = Schema101009.NS_PMDXML;
+            schemaUrl = Schema101009.SCHEMA_PMDXML;
+            schemaVer = Schema101009.VER_PMDXML;
+        }else if(this.xmlType == XmlModelFileType.XML_130128){
+            namespace = Schema130128.NS_PMDXML;
+            schemaUrl = Schema130128.SCHEMA_PMDXML;
+            schemaVer = Schema130128.VER_PMDXML;
+        }else{
+            assert false;
+            throw new AssertionError();
+        }
+
+        ind().putAttr("xmlns", namespace).ln();
+        ind().putAttr("xmlns:" + XSINS, XmlResourceResolver.NS_XSD).ln();
+
+        ind().putRawText(XSINS).putRawText(":schemaLocation=")
+             .putRawCh('"');
+        putRawText(namespace).ln();
+        ind().sp(2).putRawText(schemaUrl)
+             .putRawCh('"').ln();
+
+        ind().putAttr(PmdAttr.SCHEMA_VERSION.attr(), schemaVer);
+        ln(2);
+
+        I18nText modelName = model.getModelName();
+        ind();
+        this.exp.putPrimaryNameAttr(PmdAttr.NAME, modelName);
+        ln();
+
+        return;
+    }
+
+    /**
+     * モデル基本情報を出力する。
+     * @param model モデル情報
+     * @return this本体
+     * @throws IOException 出力エラー
+     */
+    private PmdXmlExporter putModelInfo(PmdModel model)
+            throws IOException{
+        I18nText modelName = model.getModelName();
+        this.exp.putI18nName(modelName);
+        ln();
+
+        I18nText description = model.getDescription();
+        for(String lang639 : description.lang639CodeList()){
+            String descText = description.getI18nText(lang639);
+            putDescription(lang639, descText);
+            ln();
+        }
+
+        return this;
+    }
+
+    /**
+     * モデル詳細テキストを出力する。
+     * @param lang639 言語コード
+     * @param content 詳細内容
+     * @return this本体
+     * @throws IOException 出力エラー
+     */
+    private PmdXmlExporter putDescription(CharSequence lang639,
+                                              CharSequence content)
+            throws IOException{
+        String text = content.toString();
+        text = text.replace(CRLF, LF);
+        text = text.replace(CR,   LF);
+
+        ind().putOpenSTag(PmdTag.DESCRIPTION.tag());
+        if( ! I18nText.CODE639_PRIMARY.equals(lang639) ){
+            sp().putAttr(PmdAttr.LANG.attr(), lang639).sp();
+        }
+        putCloseSTag().ln();
+
+        putBRedContent(text);
+
+        ind().putETag(PmdTag.DESCRIPTION.tag()).ln();
+
+        if( !    ExtraExporter.hasOnlyBasicLatin(text)
+              && isBasicLatinOnlyOut() ){
+            putBlockComment(text);
+        }
+
+        return this;
+    }
+
+    /**
+     * break要素を含む要素内容を出力する。
+     * 必要に応じてXML定義済み実体文字が割り振られた文字、
+     * コントロールコード、および非BasicLatin文字がエスケープされる。
+     * \nはbrタグに変換される。
+     * @param content 内容
+     * @return this本体
+     * @throws IOException 出力エラー
+     */
+    private PmdXmlExporter putBRedContent(CharSequence content)
+            throws IOException{
+        int length = content.length();
+
+        int startPos = 0;
+
+        for(int idx = 0; idx < length; idx++){
+            char ch = content.charAt(idx);
+            if(ch == '\n'){
+                CharSequence seq = content.subSequence(startPos, idx);
+                putContent(seq).putRawText("<br/>").ln();
+                startPos = idx + 1;
+            }
+        }
+
+        if(startPos < length){
+            CharSequence seq = content.subSequence(startPos, length);
+            putContent(seq).ln();
+        }
+
+        return this;
+    }
+
+    /**
+     * 各種メタ情報を出力する。
+     * @return this本体
+     * @throws IOException 出力エラー
+     */
+    private PmdXmlExporter putMetaInfo() throws IOException{
+        ind().putSimpleSTag(PmdTag.LICENSE.tag()).ln();
+        ind().putETag(PmdTag.LICENSE.tag()).ln(2);
+
+        ind().putSimpleSTag(PmdTag.CREDITS.tag()).ln();
+        ind().putETag(PmdTag.CREDITS.tag()).ln(2);
+
+        String genName = getGenerator();
+        if(genName != null){
+            ind().putOpenSTag(PmdTag.META.tag()).sp();
+            putAttr(PmdAttr.NAME.attr(), "generator").sp();
+            putAttr(PmdAttr.CONTENT.attr(), genName).sp();
+            putCloseEmpty().ln();
+        }
+
+        ind().putOpenSTag(PmdTag.META.tag()).sp();
+        putAttr(PmdAttr.NAME.attr(), "siteURL").sp();
+        putAttr(PmdAttr.CONTENT.attr(), "").sp();
+        putCloseEmpty().ln();
+
+        ind().putOpenSTag(PmdTag.META.tag()).sp();
+        putAttr(PmdAttr.NAME.attr(), "imageURL").sp();
+        putAttr(PmdAttr.CONTENT.attr(), "").sp();
+        putCloseEmpty().ln(2);
+
+        return this;
+    }
+
+}
diff --git a/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/RefHelper.java b/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/RefHelper.java
new file mode 100644 (file)
index 0000000..86d7e46
--- /dev/null
@@ -0,0 +1,478 @@
+/*
+ * SAX ID-reference helper
+ *
+ * License : The MIT License
+ * Copyright(c) 2013 MikuToga Partners
+ */
+
+package jp.sfjp.mikutoga.pmd.model.xml;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import jp.sfjp.mikutoga.pmd.model.BoneInfo;
+import jp.sfjp.mikutoga.pmd.model.Material;
+import jp.sfjp.mikutoga.pmd.model.MorphVertex;
+import jp.sfjp.mikutoga.pmd.model.RigidGroup;
+import jp.sfjp.mikutoga.pmd.model.RigidInfo;
+import jp.sfjp.mikutoga.pmd.model.ShadeInfo;
+import jp.sfjp.mikutoga.pmd.model.Surface;
+import jp.sfjp.mikutoga.pmd.model.Vertex;
+
+/**
+ * 各種ID参照解決用の一時的なヘルパ。
+ */
+class RefHelper {
+
+    // マテリアル関連
+    private final List<IdRefHolder<Material>> materialSfcGroupIdRefList =
+            new LinkedList<IdRefHolder<Material>>();
+    private final List<IdRefHolder<Material>> materialToonIdRefList =
+            new LinkedList<IdRefHolder<Material>>();
+    private final Map<String, Integer> toonIdxMap =
+            new HashMap<String, Integer>();
+
+    // ボーン関連
+    private final Map<String, BoneInfo> boneIdMap =
+            new HashMap<String, BoneInfo>();
+    private final List<IdRefHolder<BoneInfo>> boneChainIdRefList =
+            new LinkedList<IdRefHolder<BoneInfo>>();
+    private final List<IdRefHolder<BoneInfo>> boneSourceIdRefList =
+            new LinkedList<IdRefHolder<BoneInfo>>();
+
+    // モーフ関連
+    private final List<IdRefHolder<MorphVertex>> morphVertexIdRefList =
+            new LinkedList<IdRefHolder<MorphVertex>>();
+
+    // 剛体関連
+    private final Map<String, RigidInfo> rigidIdMap =
+            new HashMap<String, RigidInfo>();
+    private final Map<String, RigidGroup> rigidGroupIdMap =
+            new HashMap<String, RigidGroup>();
+    private final List<IdRefHolder<RigidInfo>> thghRigidGroupIdRefList =
+            new LinkedList<IdRefHolder<RigidInfo>>();
+
+    // 面関連
+    private final Map<String, List<Surface>> surfaceGroupIdMap =
+            new HashMap<String, List<Surface>>();
+    private final List<IdRefHolder<Surface>> surfaceVertexIdRef =
+            new LinkedList<IdRefHolder<Surface>>();
+
+    // 頂点関連
+    private final Map<String, Vertex> vertexIdMap =
+            new HashMap<String, Vertex>();
+
+
+    /**
+     * コンストラクタ。
+     */
+    RefHelper(){
+        super();
+        return;
+    }
+
+
+    /**
+     * マテリアルからのサーフェイスグループID参照を登録する。
+     * @param material マテリアル
+     * @param idRef サーフェイスグループID参照
+     */
+    void addSurfaceGroupIdRef(Material material, String idRef){
+        IdRefHolder<Material> holder =
+                new IdRefHolder<Material>(material, idRef);
+        this.materialSfcGroupIdRefList.add(holder);
+
+        return;
+    }
+
+    /**
+     * サーフェイスグループの構成サーフェイスを登録する。
+     * @param surfaceGroupId サーフェイスグループID
+     * @param surface サーフェイス
+     */
+    void addSurfaceGroup(String surfaceGroupId, Surface surface){
+        List<Surface> surfaceGroup =
+                this.surfaceGroupIdMap.get(surfaceGroupId);
+        if(surfaceGroup == null){
+            surfaceGroup = new LinkedList<Surface>();
+            this.surfaceGroupIdMap.put(surfaceGroupId, surfaceGroup);
+        }
+
+        surfaceGroup.add(surface);
+
+        return;
+    }
+
+    /**
+     * マテリアルからのサーフェイスグループID参照を解決する。
+     */
+    void resolveMaterialSurfaceGroupId(){
+        for(IdRefHolder<Material> holder : this.materialSfcGroupIdRefList){
+            Material material = holder.getBody();
+            String surfaceGroupIdRef = holder.getIdRef();
+
+            List<Surface> surfaceGroup =
+                    this.surfaceGroupIdMap.get(surfaceGroupIdRef);
+
+            List<Surface> surfaceList = material.getSurfaceList();
+            surfaceList.addAll(surfaceGroup);
+        }
+
+        return;
+    }
+
+    /**
+     * マテリアルからの共有トゥーンファイルID参照を追加登録する。
+     * @param material マテリアル
+     * @param idRef トゥーンファイルID参照
+     */
+    void addToonFileIdRef(Material material, String idRef){
+        IdRefHolder<Material> holder =
+                new IdRefHolder<Material>(material, idRef);
+        this.materialToonIdRefList.add(holder);
+        return;
+    }
+
+    /**
+     * 共有トゥーンファイルのインデックスを登録する。
+     * @param toonFileId ToonファイルID
+     * @param idx 共有Toonインデックス
+     */
+    void addToonIdx(String toonFileId, int idx){
+        this.toonIdxMap.put(toonFileId, idx);
+        return;
+    }
+
+    /**
+     * マテリアルからの共有トゥーンインデックスの参照問題を解決する。
+     */
+    void resolveToonIdx(){
+        for(IdRefHolder<Material> holder : this.materialToonIdRefList){
+            Material material = holder.getBody();
+            String toonFileIdRef = holder.getIdRef();
+            int toonIdx = this.toonIdxMap.get(toonFileIdRef);
+
+            ShadeInfo shadeInfo = material.getShadeInfo();
+            shadeInfo.setToonIndex(toonIdx);
+        }
+
+        return;
+    }
+
+    /**
+     * ボーンIDを登録する。
+     * @param boneId ボーンID
+     * @param boneInfo ボーン情報
+     */
+    void addBoneId(String boneId, BoneInfo boneInfo){
+        this.boneIdMap.put(boneId, boneInfo);
+        return;
+    }
+
+    /**
+     * ボーンIDを問い合わせる。
+     * @param boneId ボーンID
+     * @return ボーン情報
+     */
+    BoneInfo findBoneId(String boneId){
+        BoneInfo result = this.boneIdMap.get(boneId);
+        return result;
+    }
+
+    /**
+     * ボーン間チェーン参照情報を登録する。
+     * @param bone ボーン情報
+     * @param prevBoneIdRef 前ボーンID参照
+     * @param nextBoneIdRef 次ボーンID参照
+     */
+    void addBoneChain(BoneInfo bone,
+                        String prevBoneIdRef, String nextBoneIdRef ){
+        IdRefHolder<BoneInfo> holder =
+                new IdRefHolder<BoneInfo>(bone, prevBoneIdRef, nextBoneIdRef);
+        this.boneChainIdRefList.add(holder);
+        return;
+    }
+
+    /**
+     * ボーン間チェーン参照情報を解決する。
+     */
+    void resolveBoneChainIdRef(){
+        for(IdRefHolder<BoneInfo> holder : this.boneChainIdRefList){
+            BoneInfo bone = holder.getBody();
+            String prevBoneIdRef = holder.getIdRef();
+            String nextBoneIdRef = holder.getIdRef2();
+
+            if(prevBoneIdRef != null){
+                BoneInfo prevBone = this.boneIdMap.get(prevBoneIdRef);
+                bone.setPrevBone(prevBone);
+            }
+
+            if(nextBoneIdRef != null){
+                BoneInfo nextBone = this.boneIdMap.get(nextBoneIdRef);
+                bone.setNextBone(nextBone);
+            }
+        }
+
+        return;
+    }
+
+    /**
+     * ボーン情報からのソースボーンID参照を登録する。
+     * @param bone ボーン情報
+     * @param srcBoneIdRef ソースボーンID参照
+     */
+    void addSrcBoneIdRef(BoneInfo bone, String srcBoneIdRef){
+        IdRefHolder<BoneInfo> holder =
+                new IdRefHolder<BoneInfo>(bone, srcBoneIdRef);
+        this.boneSourceIdRefList.add(holder);
+        return;
+    }
+
+    /**
+     * ボーン情報からのソースボーンID参照を解決する。
+     */
+    void resolveSrcBoneIdRef(){
+        for(IdRefHolder<BoneInfo> holder : this.boneSourceIdRefList){
+            BoneInfo bone = holder.getBody();
+            String srcBoneIdRef = holder.getIdRef();
+            if(srcBoneIdRef == null) continue;
+
+            BoneInfo srcBone = this.boneIdMap.get(srcBoneIdRef);
+            bone.setSrcBone(srcBone);
+        }
+
+        return;
+    }
+
+    /**
+     * モーフ頂点からの頂点ID参照を登録する。
+     * @param morphVertex モーフ頂点
+     * @param vertexIdRef 頂点ID参照
+     */
+    void addMorphVertexIdRef(MorphVertex morphVertex, String vertexIdRef){
+        IdRefHolder<MorphVertex> holder =
+                new IdRefHolder<MorphVertex>(morphVertex, vertexIdRef);
+        this.morphVertexIdRefList.add(holder);
+        return;
+    }
+
+    /**
+     * 剛体IDを登録する。
+     * @param rigidId 剛体ID
+     * @param rigid 剛体情報
+     */
+    void addRigidId(String rigidId, RigidInfo rigid){
+        this.rigidIdMap.put(rigidId, rigid);
+        return;
+    }
+
+    /**
+     * 剛体IDを問い合わせる。
+     * @param rigidId 剛体ID
+     * @return 剛体情報
+     */
+    RigidInfo findRigidId(String rigidId){
+        RigidInfo result = this.rigidIdMap.get(rigidId);
+        return result;
+    }
+
+    /**
+     * 剛体からの通過剛体グループID参照を登録する。
+     * @param rigid 剛体情報
+     * @param rigidGroupIdRef 剛体グループID参照
+     */
+    void addThroughRigidGroupIdRef(RigidInfo rigid, String rigidGroupIdRef){
+        IdRefHolder<RigidInfo> holder =
+                new IdRefHolder<RigidInfo>(rigid, rigidGroupIdRef);
+        this.thghRigidGroupIdRefList.add(holder);
+        return;
+    }
+
+    /**
+     * 剛体グループIDを登録する。
+     * @param rigidGroupId 剛体グループID
+     * @param rigidGroup 剛体グループ
+     */
+    void addRigidGroupId(String rigidGroupId, RigidGroup rigidGroup){
+        this.rigidGroupIdMap.put(rigidGroupId, rigidGroup);
+        return;
+    }
+
+    /**
+     * 剛体グループID参照を解決する。
+     */
+    void resolveThroughRigidGroupIdRef(){
+        for(IdRefHolder<RigidInfo> holder : this.thghRigidGroupIdRefList){
+            RigidInfo rigid = holder.getBody();
+            String rigidGroupIdRef = holder.getIdRef();
+            RigidGroup group = this.rigidGroupIdMap.get(rigidGroupIdRef);
+
+            Collection<RigidGroup> throughGroups =
+                    rigid.getThroughGroupColl();
+            throughGroups.add(group);
+        }
+
+        return;
+    }
+
+    /**
+     * 三角ポリゴンサーフェイス面からの頂点ID参照を登録する。
+     * @param surface surface面
+     * @param vtxIdRef1 頂点ID参照その1
+     * @param vtxIdRef2 頂点ID参照その2
+     * @param vtxIdRef3 頂点ID参照その3
+     */
+    void addSurfaceVertex(Surface surface,
+                            String vtxIdRef1,
+                            String vtxIdRef2,
+                            String vtxIdRef3 ){
+        IdRefHolder<Surface> holder =
+                new IdRefHolder<Surface>(surface,
+                                         vtxIdRef1,
+                                         vtxIdRef2,
+                                         vtxIdRef3 );
+
+        this.surfaceVertexIdRef.add(holder);
+
+        return;
+    }
+
+    /**
+     * 頂点IDを登録する。
+     * @param vertexId 頂点ID
+     * @param vertex 頂点
+     */
+    void addVertexId(String vertexId, Vertex vertex){
+        this.vertexIdMap.put(vertexId, vertex);
+        return;
+    }
+
+    /**
+     * モーフ頂点からの頂点ID参照を解決する。
+     */
+    void resolveMorphVertexIdRef(){
+        for(IdRefHolder<MorphVertex> holder : this.morphVertexIdRefList){
+            MorphVertex morphVertex = holder.getBody();
+            String vertexIdRef = holder.getIdRef();
+            Vertex vertex = this.vertexIdMap.get(vertexIdRef);
+
+            morphVertex.setBaseVertex(vertex);
+        }
+
+        return;
+    }
+
+    /**
+     * サーフェイスからの頂点ID参照を解決する。
+     */
+    void resolveSurfaceVertexIdRef(){
+        for(IdRefHolder<Surface> holder : this.surfaceVertexIdRef){
+            Surface surface = holder.getBody();
+            String vertexIdRef1 = holder.getIdRef();
+            String vertexIdRef2 = holder.getIdRef2();
+            String vertexIdRef3 = holder.getIdRef3();
+
+            Vertex vtx1 = this.vertexIdMap.get(vertexIdRef1);
+            Vertex vtx2 = this.vertexIdMap.get(vertexIdRef2);
+            Vertex vtx3 = this.vertexIdMap.get(vertexIdRef3);
+
+            surface.setTriangle(vtx1, vtx2, vtx3);
+        }
+
+        return;
+    }
+
+    /**
+     * ID参照解決用一時ホルダ。
+     * 必要に応じて参照IDを3つまで持てる。
+     * @param <E> ID参照元インスタンス型
+     */
+    private static final class IdRefHolder<E> {
+
+        private final E body;
+
+        private final String idRef;
+        private final String idRef2;
+        private final String idRef3;
+
+        /**
+         * コンストラクタ。
+         * @param body ID参照元インスタンス
+         * @param idRef 参照ID
+         */
+        IdRefHolder(E body, String idRef){
+            this(body, idRef, null, null);
+            return;
+        }
+
+        /**
+         * コンストラクタ。
+         * @param body ID参照元インスタンス
+         * @param idRef 参照ID
+         * @param idRef2 参照IDその2
+         */
+        IdRefHolder(E body, String idRef, String idRef2){
+            this(body, idRef, idRef2, null);
+            return;
+        }
+
+        /**
+         * コンストラクタ。
+         * @param body ID参照元インスタンス
+         * @param idRef  参照ID
+         * @param idRef2 参照IDその2
+         * @param idRef3 参照IDその3
+         */
+        IdRefHolder(E body,
+                      String idRef,
+                      String idRef2,
+                      String idRef3 ){
+            super();
+
+            this.body = body;
+
+            this.idRef  = idRef;
+            this.idRef2 = idRef2;
+            this.idRef3 = idRef3;
+
+            return;
+        }
+
+
+        /**
+         * ID参照元インスタンスを返す。
+         * @return ID参照元インスタンス
+         */
+        private E getBody(){
+            return this.body;
+        }
+
+        /**
+         * 参照IDを返す。
+         * @return 参照ID
+         */
+        private String getIdRef(){
+            return this.idRef;
+        }
+
+        /**
+         * 参照IDその2を返す。
+         * @return 参照ID
+         */
+        private String getIdRef2(){
+            return this.idRef2;
+        }
+
+        /**
+         * 参照IDその3を返す。
+         * @return 参照ID
+         */
+        private String getIdRef3(){
+            return this.idRef3;
+        }
+
+    }
+
+}
diff --git a/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/SaxBoneListener.java b/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/SaxBoneListener.java
new file mode 100644 (file)
index 0000000..8e3b7e0
--- /dev/null
@@ -0,0 +1,307 @@
+/*
+ * bone listener from XML
+ *
+ * License : The MIT License
+ * Copyright(c) 2013 MikuToga Partners
+ */
+
+package jp.sfjp.mikutoga.pmd.model.xml;
+
+import java.util.List;
+import jp.sfjp.mikutoga.corelib.I18nText;
+import jp.sfjp.mikutoga.math.MkPos3D;
+import jp.sfjp.mikutoga.pmd.BoneType;
+import jp.sfjp.mikutoga.pmd.model.BoneGroup;
+import jp.sfjp.mikutoga.pmd.model.BoneInfo;
+import jp.sfjp.mikutoga.pmd.model.IKChain;
+import jp.sfjp.mikutoga.pmd.model.ListUtil;
+
+/*
+    + boneList
+        + bone
+            + i18nName
+            + position
+            + rotationRatio
+            + ikBone
+            + sourceBone
+            + boneChain
+    + boneGroupList
+        + boneGroup
+            + i18nName
+            + boneGroupMember
+    + ikChainList
+        + ikChain
+            + chainOrder
+*/
+
+/**
+ * ボーン関連のXML要素出現イベントを受信する。
+ */
+class SaxBoneListener extends SaxListener{
+
+    private final RefHelper helper;
+
+    private BoneInfo currentBone = null;
+
+    private BoneGroup currentBoneGroup = null;
+
+    private IKChain currentIkChain = null;
+
+
+    /**
+     * コンストラクタ。
+     * @param helper 参照ヘルパ
+     */
+    SaxBoneListener(RefHelper helper) {
+        super();
+        this.helper = helper;
+        return;
+    }
+
+
+    /**
+     * boneInfoタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.BONE)
+    void openBoneInfo(){
+        this.currentBone = new BoneInfo();
+
+        String nameAttr = getStringAttr(PmdAttr.NAME);
+        I18nText boneName = this.currentBone.getBoneName();
+        boneName.setPrimaryText(nameAttr);
+
+        String boneId = getStringAttr(PmdAttr.BONE_ID);
+        this.helper.addBoneId(boneId, this.currentBone);
+
+        String typeAttr = getStringAttr(PmdAttr.TYPE);
+        BoneType boneType = BoneType.valueOf(typeAttr);
+        this.currentBone.setBoneType(boneType);
+
+        return;
+    }
+
+    /**
+     * boneInfoタグ終了の通知を受け取る。
+     */
+    @CloseXmlMark(PmdTag.BONE)
+    void closeBoneInfo(){
+        List<BoneInfo> boneList = getPmdModel().getBoneList();
+        boneList.add(this.currentBone);
+
+        this.currentBone = null;
+
+        return;
+    }
+
+    /**
+     * i18nTextタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.I18N_NAME)
+    void openI18nText(){
+        I18nText i18nName;
+        if(this.currentBone != null){
+            i18nName = this.currentBone.getBoneName();
+        }else if(this.currentBoneGroup != null){
+            i18nName = this.currentBoneGroup.getGroupName();
+        }else{
+            return;
+        }
+
+        String lang = getStringAttr(PmdAttr.LANG);
+        String name = getStringAttr(PmdAttr.NAME);
+
+        i18nName.setI18nText(lang, name);
+
+        return;
+    }
+
+    /**
+     * boneListタグ終了の通知を受け取る。
+     */
+    @CloseXmlMark(PmdTag.BONE_LIST)
+    void closeBoneList(){
+        this.helper.resolveSrcBoneIdRef();
+        this.helper.resolveBoneChainIdRef();
+
+        List<BoneInfo> boneList = getPmdModel().getBoneList();
+        ListUtil.assignIndexedSerial(boneList);
+
+        return;
+    }
+
+    /**
+     * positionタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.POSITION)
+    void openPosition(){
+        float x = getFloatAttr(PmdAttr.X);
+        float y = getFloatAttr(PmdAttr.Y);
+        float z = getFloatAttr(PmdAttr.Z);
+
+        MkPos3D pos = this.currentBone.getPosition();
+        pos.setPosition(x, y, z);
+
+        return;
+    }
+
+    /**
+     * rotationRatioタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.ROTATION_RATIO)
+    void openRotationRatio(){
+        int ratio = getIntAttr(PmdAttr.RATIO);
+        this.currentBone.setRotationRatio(ratio);
+        return;
+    }
+
+    /**
+     * ikBoneタグ開始の通知を受け取る。
+     * ※ 101009版スキーマでしか出現しない。
+     */
+    @OpenXmlMark(PmdTag.IK_BONE) // 101009 only
+    void openIkBone(){
+        openSrcBone();
+        return;
+    }
+
+    /**
+     * sourceBoneタグ開始の通知を受け取る。
+     * ※ 130128版スキーマでしか出現しない。
+     */
+    @OpenXmlMark(PmdTag.SOURCE_BONE) // 130128 only
+    void openSrcBone(){
+        String boneIdRef = getStringAttr(PmdAttr.BONE_IDREF);
+        this.helper.addSrcBoneIdRef(this.currentBone, boneIdRef);
+        return;
+    }
+
+    /**
+     * boneChainタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.BONE_CHAIN)
+    void openBoneChain(){
+        String prevBoneIdRef = getStringAttr(PmdAttr.PREV_BONE_IDREF);
+        String nextBoneIdRef = getStringAttr(PmdAttr.NEXT_BONE_IDREF);
+
+        this.helper.addBoneChain(this.currentBone,
+                                 prevBoneIdRef, nextBoneIdRef );
+
+        return;
+    }
+
+    /**
+     * boneGroupListタグ開始の通知を受け取る。
+     * 暗黙のデフォルトボーングループが無条件に格納される。
+     */
+    @OpenXmlMark(PmdTag.BONE_GROUP_LIST)
+    void openBoneGroupList(){
+        BoneGroup defaultBoneGroup = new BoneGroup();
+        defaultBoneGroup.setSerialNumber(0);
+
+        List<BoneGroup> boneGroupList =
+                this.getPmdModel().getBoneGroupList();
+        boneGroupList.add(defaultBoneGroup);
+
+        return;
+    }
+
+    /**
+     * boneGroupListタグ終了の通知を受け取る。
+     */
+    @CloseXmlMark(PmdTag.BONE_GROUP_LIST)
+    void closeBoneGroupList(){
+        List<BoneGroup> boneGroupList = getPmdModel().getBoneGroupList();
+        ListUtil.assignIndexedSerial(boneGroupList);
+        return;
+    }
+
+    /**
+     * boneGroupタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.BONE_GROUP)
+    void openBoneGroup(){
+        this.currentBoneGroup = new BoneGroup();
+
+        String nameAttr = getStringAttr(PmdAttr.NAME);
+        I18nText groupName = this.currentBoneGroup.getGroupName();
+        groupName.setPrimaryText(nameAttr);
+
+        return;
+    }
+
+    /**
+     * boneGroupタグ終了の通知を受け取る。
+     */
+    @CloseXmlMark(PmdTag.BONE_GROUP)
+    void closeBoneGroup(){
+        List<BoneGroup> boneGroupList = getPmdModel().getBoneGroupList();
+        boneGroupList.add(this.currentBoneGroup);
+
+        this.currentBoneGroup = null;
+
+        return;
+    }
+
+    /**
+     * boneGroupMemberタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.BONE_GROUP_MEMBER)
+    void openBoneGroupMember(){
+        String boneIdRef = getStringAttr(PmdAttr.BONE_IDREF);
+
+        BoneInfo bone = this.helper.findBoneId(boneIdRef);
+
+        List<BoneInfo> boneList = this.currentBoneGroup.getBoneList();
+        boneList.add(bone);
+
+        return;
+    }
+
+    /**
+     * ikChainタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.IK_CHAIN)
+    void openIkChain(){
+        this.currentIkChain = new IKChain();
+
+        String ikBoneIdRef = getStringAttr(PmdAttr.IK_BONE_IDREF);
+        BoneInfo bone = this.helper.findBoneId(ikBoneIdRef);
+        this.currentIkChain.setIkBone(bone);
+
+        int depth = getIntAttr(PmdAttr.RECURSIVE_DEPTH);
+        this.currentIkChain.setIKDepth(depth);
+
+        float weight = getFloatAttr(PmdAttr.WEIGHT);
+        this.currentIkChain.setIKWeight(weight);
+
+        return;
+    }
+
+    /**
+     * ikChainタグ終了の通知を受け取る。
+     */
+    @CloseXmlMark(PmdTag.IK_CHAIN)
+    void closeIkChain(){
+        List<IKChain> ikChainList = getPmdModel().getIKChainList();
+        ikChainList.add(this.currentIkChain);
+
+        this.currentIkChain = null;
+
+        return;
+    }
+
+    /**
+     * chainOrderタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.CHAIN_ORDER)
+    void openChainOrder(){
+        String boneIdRef = getStringAttr(PmdAttr.BONE_IDREF);
+        BoneInfo bone = this.helper.findBoneId(boneIdRef);
+
+        List<BoneInfo> chainList = this.currentIkChain.getChainedBoneList();
+        chainList.add(bone);
+
+        return;
+    }
+
+}
diff --git a/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/SaxDynamicsListener.java b/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/SaxDynamicsListener.java
new file mode 100644 (file)
index 0000000..e8b839e
--- /dev/null
@@ -0,0 +1,479 @@
+/*
+ * dynamics listener from XML
+ *
+ * License : The MIT License
+ * Copyright(c) 2013 MikuToga Partners
+ */
+
+package jp.sfjp.mikutoga.pmd.model.xml;
+
+import java.util.List;
+import jp.sfjp.mikutoga.corelib.I18nText;
+import jp.sfjp.mikutoga.math.MkPos3D;
+import jp.sfjp.mikutoga.pmd.Deg3d;
+import jp.sfjp.mikutoga.pmd.Rad3d;
+import jp.sfjp.mikutoga.pmd.RigidBehaviorType;
+import jp.sfjp.mikutoga.pmd.RigidShapeType;
+import jp.sfjp.mikutoga.pmd.TripletRange;
+import jp.sfjp.mikutoga.pmd.model.BoneInfo;
+import jp.sfjp.mikutoga.pmd.model.DynamicsInfo;
+import jp.sfjp.mikutoga.pmd.model.JointInfo;
+import jp.sfjp.mikutoga.pmd.model.ListUtil;
+import jp.sfjp.mikutoga.pmd.model.RigidGroup;
+import jp.sfjp.mikutoga.pmd.model.RigidInfo;
+import jp.sfjp.mikutoga.pmd.model.RigidShape;
+
+/*
+    + rigidList
+        + rigid
+            + i18nName
+            + linkedBone
+            + rigidShapeSphere
+            + rigidShapeBox
+            + rigidShapeCapsule
+            + position
+            + radRotation
+            + dynamics
+            + throughRigidGroup
+    + rigidGroupList
+        + rigidGroup
+            + rigidGroupMember
+    + jointList
+        + joint
+            + i18nName
+            + jointedRigidPair
+            + position
+            + limitPosition
+            + radRotation
+            + limitRotation
+            + elasticPosition
+            + elasticRotation
+*/
+
+/**
+ * 剛体力学関連のXML要素出現イベントを受信する。
+ */
+class SaxDynamicsListener extends SaxListener{
+
+    private final RefHelper helper;
+
+    private RigidInfo currentRigid = null;
+    private RigidGroup currentRigidGroup = null;
+    private JointInfo currentJoint = null;
+
+
+    /**
+     * コンストラクタ。
+     * @param helper 参照ヘルパ
+     */
+    SaxDynamicsListener(RefHelper helper) {
+        super();
+        this.helper = helper;
+        return;
+    }
+
+
+    /**
+     * rigidListタグ終了の通知を受け取る。
+     */
+    @CloseXmlMark(PmdTag.RIGID_LIST)
+    void closeRigidList(){
+        List<RigidInfo> rigidList = getPmdModel().getRigidList();
+        ListUtil.assignIndexedSerial(rigidList);
+        return;
+    }
+
+    /**
+     * rigidタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.RIGID)
+    void openRigid(){
+        this.currentRigid = new RigidInfo();
+
+        String name = getStringAttr(PmdAttr.NAME);
+        I18nText rigidName = this.currentRigid.getRigidName();
+        rigidName.setPrimaryText(name);
+
+        String rigidId = getStringAttr(PmdAttr.RIGID_ID);
+        this.helper.addRigidId(rigidId, this.currentRigid);
+
+        String behavior = getStringAttr(PmdAttr.BEHAVIOR);
+        RigidBehaviorType type = RigidBehaviorType.valueOf(behavior);
+        this.currentRigid.setBehaviorType(type);
+
+        return;
+    }
+
+    /**
+     * rigidタグ終了の通知を受け取る。
+     */
+    @CloseXmlMark(PmdTag.RIGID)
+    void closeRigid(){
+        List<RigidInfo> rigidInfoList = getPmdModel().getRigidList();
+        rigidInfoList.add(this.currentRigid);
+
+        this.currentRigid = null;
+
+        return;
+    }
+
+    /**
+     * i18nTextタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.I18N_NAME)
+    void openI18nText(){
+        I18nText i18nName;
+        if(this.currentRigid != null){
+            i18nName = this.currentRigid.getRigidName();
+        }else if(this.currentJoint != null){
+            i18nName = this.currentJoint.getJointName();
+        }else{
+            assert false;
+            throw new AssertionError();
+        }
+
+        String lang = getStringAttr(PmdAttr.LANG);
+        String name = getStringAttr(PmdAttr.NAME);
+
+        i18nName.setI18nText(lang, name);
+
+        return;
+    }
+
+    /**
+     * linkedBoneタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.LINKED_BONE)
+    void openLinkedBone(){
+        String boneIdRef = getStringAttr(PmdAttr.BONE_IDREF);
+        BoneInfo linkedBone = this.helper.findBoneId(boneIdRef);
+        this.currentRigid.setLinkedBone(linkedBone);
+
+        return;
+    }
+
+    /**
+     * rigidShapeSphereタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.RIGID_SHAPE_SPHERE)
+    void openRigidShapeSphere(){
+        RigidShape shape = this.currentRigid.getRigidShape();
+
+        shape.setShapeType(RigidShapeType.SPHERE);
+
+        float radius = getFloatAttr(PmdAttr.RADIUS);
+        shape.setRadius(radius);
+
+        return;
+    }
+
+    /**
+     * rigidShapeBoxタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.RIGID_SHAPE_BOX)
+    void openRigidShapeBox(){
+        RigidShape shape = this.currentRigid.getRigidShape();
+
+        shape.setShapeType(RigidShapeType.BOX);
+
+        float width  = getFloatAttr(PmdAttr.WIDTH);
+        float height = getFloatAttr(PmdAttr.HEIGHT);
+        float depth  = getFloatAttr(PmdAttr.DEPTH);
+
+        shape.setWidth(width);
+        shape.setHeight(height);
+        shape.setDepth(depth);
+
+        return;
+    }
+
+    /**
+     * rigidShapeCapsuleタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.RIGID_SHAPE_CAPSULE)
+    void openRigidShapeCapsule(){
+        RigidShape shape = this.currentRigid.getRigidShape();
+
+        shape.setShapeType(RigidShapeType.CAPSULE);
+
+        float height = getFloatAttr(PmdAttr.HEIGHT);
+        float radius = getFloatAttr(PmdAttr.RADIUS);
+
+        shape.setHeight(height);
+        shape.setRadius(radius);
+
+        return;
+    }
+
+    /**
+     * positionタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.POSITION)
+    void openPosition(){
+        MkPos3D pos;
+        if(this.currentRigid != null){
+            pos = this.currentRigid.getPosition();
+        }else if(this.currentJoint != null){
+            pos = this.currentJoint.getPosition();
+        }else{
+            assert false;
+            throw new AssertionError();
+        }
+
+        float x = getFloatAttr(PmdAttr.X);
+        float y = getFloatAttr(PmdAttr.Y);
+        float z = getFloatAttr(PmdAttr.Z);
+
+        pos.setPosition(x, y, z);
+
+        return;
+    }
+
+    /**
+     * radRotationタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.RAD_ROTATION)
+    void openRadRotation(){
+        Rad3d rad;
+        if(this.currentRigid != null){
+            rad = this.currentRigid.getRotation();
+        }else if(this.currentJoint != null){
+            rad = this.currentJoint.getRotation();
+        }else{
+            assert false;
+            throw new AssertionError();
+        }
+
+        float xRad = getFloatAttr(PmdAttr.X_RAD);
+        float yRad = getFloatAttr(PmdAttr.Y_RAD);
+        float zRad = getFloatAttr(PmdAttr.Z_RAD);
+
+        rad.setXRad(xRad);
+        rad.setYRad(yRad);
+        rad.setZRad(zRad);
+
+        return;
+    }
+
+    /**
+     * dynamicsタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.DYNAMICS)
+    void openDynamics(){
+
+        float mass            = getFloatAttr(PmdAttr.MASS);
+        float dampingPosition = getFloatAttr(PmdAttr.DAMPING_POSITION);
+        float dampingRotation = getFloatAttr(PmdAttr.DAMPING_ROTATION);
+        float restitution     = getFloatAttr(PmdAttr.RESTITUTION);
+        float friction        = getFloatAttr(PmdAttr.FRICTION);
+
+        DynamicsInfo dynamics = this.currentRigid.getDynamicsInfo();
+
+        dynamics.setMass(mass);
+        dynamics.setDampingPosition(dampingPosition);
+        dynamics.setDampingRotation(dampingRotation);
+        dynamics.setRestitution(restitution);
+        dynamics.setFriction(friction);
+
+        return;
+    }
+
+    /**
+     * throughRigidGroupタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.THROUGH_RIGID_GROUP)
+    void openThroughRigidGroup(){
+        String rigidGroupIdRef =
+                getStringAttr(PmdAttr.RIGID_GROUP_IDREF);
+
+        this.helper.addThroughRigidGroupIdRef(this.currentRigid,
+                                              rigidGroupIdRef );
+
+        return;
+    }
+
+    /**
+     * rigidGroupタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.RIGID_GROUP)
+    void openRigidGroup(){
+        RigidGroup rigidGroup = new RigidGroup();
+        this.currentRigidGroup = rigidGroup;
+
+        String rigidGroupId = getStringAttr(PmdAttr.RIGID_GROUP_ID);
+
+        this.helper.addRigidGroupId(rigidGroupId, this.currentRigidGroup);
+
+        return;
+    }
+
+    /**
+     * rigidGroupタグ終了の通知を受け取る。
+     */
+    @CloseXmlMark(PmdTag.RIGID_GROUP)
+    void closeRigidGroup(){
+        List<RigidGroup> rigidGroupList = getPmdModel().getRigidGroupList();
+        rigidGroupList.add(this.currentRigidGroup);
+
+        this.currentRigidGroup = null;
+
+        return;
+    }
+
+    /**
+     * rigidGroupMemberタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.RIGID_GROUP_MEMBER)
+    void openRigidGroupMember(){
+        String rigidIdRef = getStringAttr(PmdAttr.RIGID_IDREF);
+
+        RigidInfo member = this.helper.findRigidId(rigidIdRef);
+
+        List<RigidInfo> memberList = this.currentRigidGroup.getRigidList();
+        memberList.add(member);
+        member.setRigidGroup(this.currentRigidGroup);
+
+        return;
+    }
+
+    /**
+     * rigidGroupListタグ終了の通知を受け取る。
+     * 剛体グループ総数が定員に満たない場合は自動追加される。
+     */
+    @CloseXmlMark(PmdTag.RIGID_GROUP_LIST)
+    void closeRigidGroupList(){
+
+        this.helper.resolveThroughRigidGroupIdRef();
+
+        List<RigidGroup> rigidGroupList = getPmdModel().getRigidGroupList();
+
+        while(rigidGroupList.size() < RigidGroup.MAX_RIGID_GROUP){
+            RigidGroup group = new RigidGroup();
+            rigidGroupList.add(group);
+        }
+
+        ListUtil.assignIndexedSerial(rigidGroupList);
+
+        return;
+    }
+
+    /**
+     * jointタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.JOINT)
+    void openJoint(){
+        this.currentJoint = new JointInfo();
+
+        String name = getStringAttr(PmdAttr.NAME);
+        I18nText jointName = this.currentJoint.getJointName();
+        jointName.setPrimaryText(name);
+
+        return;
+    }
+
+    /**
+     * jointタグ終了の通知を受け取る。
+     */
+    @CloseXmlMark(PmdTag.JOINT)
+    void closeJoint(){
+        List<JointInfo> jointList = getPmdModel().getJointList();
+        jointList.add(this.currentJoint);
+
+        this.currentJoint = null;
+
+        return;
+    }
+
+    /**
+     * jointedRigidPairタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.JOINTED_RIGID_PAIR)
+    void openJointedRigidPair(){
+        String rigidIdRef1 = getStringAttr(PmdAttr.RIGID_IDREF_1);
+        String rigidIdRef2 = getStringAttr(PmdAttr.RIGID_IDREF_2);
+
+        RigidInfo rigidA = this.helper.findRigidId(rigidIdRef1);
+        RigidInfo rigidB = this.helper.findRigidId(rigidIdRef2);
+
+        this.currentJoint.setRigidPair(rigidA, rigidB);
+
+        return;
+    }
+
+    /**
+     * limitPositionタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.LIMIT_POSITION)
+    void openLimitPosition(){
+        float xFrom = getFloatAttr(PmdAttr.X_FROM);
+        float xTo   = getFloatAttr(PmdAttr.X_TO);
+
+        float yFrom = getFloatAttr(PmdAttr.Y_FROM);
+        float yTo   = getFloatAttr(PmdAttr.Y_TO);
+
+        float zFrom = getFloatAttr(PmdAttr.Z_FROM);
+        float zTo   = getFloatAttr(PmdAttr.Z_TO);
+
+        TripletRange limitPos = this.currentJoint.getPositionRange();
+        limitPos.setXRange(xFrom, xTo);
+        limitPos.setYRange(yFrom, yTo);
+        limitPos.setZRange(zFrom, zTo);
+
+        return;
+    }
+
+    /**
+     * limitRotationタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.LIMIT_ROTATION)
+    void openLimitRotation(){
+        float xFrom = getFloatAttr(PmdAttr.X_FROM);
+        float xTo   = getFloatAttr(PmdAttr.X_TO);
+
+        float yFrom = getFloatAttr(PmdAttr.Y_FROM);
+        float yTo   = getFloatAttr(PmdAttr.Y_TO);
+
+        float zFrom = getFloatAttr(PmdAttr.Z_FROM);
+        float zTo   = getFloatAttr(PmdAttr.Z_TO);
+
+        TripletRange limitRot = this.currentJoint.getRotationRange();
+        limitRot.setXRange(xFrom, xTo);
+        limitRot.setYRange(yFrom, yTo);
+        limitRot.setZRange(zFrom, zTo);
+
+        return;
+    }
+
+    /**
+     * elasticPositionタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.ELASTIC_POSITION)
+    void openElasticPosition(){
+        float x = getFloatAttr(PmdAttr.X);
+        float y = getFloatAttr(PmdAttr.Y);
+        float z = getFloatAttr(PmdAttr.Z);
+
+        MkPos3D pos = this.currentJoint.getElasticPosition();
+        pos.setPosition(x, y, z);
+
+        return;
+    }
+
+    /**
+     * elasticRotationタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.ELASTIC_ROTATION)
+    void openElasticRotation(){
+        float xDeg = getFloatAttr(PmdAttr.X_DEG);
+        float yDeg = getFloatAttr(PmdAttr.Y_DEG);
+        float zDeg = getFloatAttr(PmdAttr.Z_DEG);
+
+        Deg3d rot = this.currentJoint.getElasticRotation();
+        rot.setXDeg(xDeg);
+        rot.setYDeg(yDeg);
+        rot.setZDeg(zDeg);
+
+        return;
+    }
+
+}
diff --git a/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/SaxListener.java b/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/SaxListener.java
new file mode 100644 (file)
index 0000000..96e4974
--- /dev/null
@@ -0,0 +1,247 @@
+/*
+ * PMD-SAX element listsner
+ *
+ * License : The MIT License
+ * Copyright(c) 2013 MikuToga Partners
+ */
+
+package jp.sfjp.mikutoga.pmd.model.xml;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Collection;
+import java.util.EnumMap;
+import java.util.LinkedList;
+import java.util.Map;
+import javax.xml.bind.DatatypeConverter;
+import jp.sfjp.mikutoga.pmd.model.PmdModel;
+import org.xml.sax.Attributes;
+
+/**
+ * XML要素出現の通知受信部の共通実装。
+ */
+class SaxListener {
+
+    private final Map<PmdTag, Method> openDispatcher;
+    private final Map<PmdTag, Method> closeDispatcher;
+
+    private PmdModel pmdModel = null;
+    private Attributes currentAttribute = null;
+
+
+    /**
+     * コンストラクタ。
+     */
+    protected SaxListener(){
+        super();
+
+        Class<?> thisClass = this.getClass();
+        this.openDispatcher  = getOpenDispatcher(thisClass);
+        this.closeDispatcher = getCloseDispatcher(thisClass);
+
+        return;
+    }
+
+
+    /**
+     * 指定された注釈がマークされたインスタンスメソッド群を返す。
+     * @param klass クラス
+     * @param filter 注釈
+     * @return インスタンスメソッド群
+     */
+    private static Collection<Method> filtMethod(Class<?> klass,
+                                   Class<? extends Annotation> filter ){
+        Collection<Method> result = new LinkedList<Method>();
+
+        for(Method method : klass.getDeclaredMethods()){
+            int modifiers = method.getModifiers();
+            if(Modifier.isStatic(modifiers)) continue;
+            if(Modifier.isPrivate(modifiers)) continue;
+            if(method.getParameterTypes().length > 0) continue;
+
+            Annotation anno = method.getAnnotation(filter);
+            if(anno == null) continue;
+
+            result.add(method);
+        }
+
+        return result;
+    }
+
+    /**
+     * 注釈でマークされた開始タグ通知用ディスパッチテーブルを返す。
+     * @param klass 対象クラス
+     * @return ディスパッチテーブル
+     */
+    private static Map<PmdTag, Method> getOpenDispatcher(Class<?> klass){
+        Map<PmdTag, Method> result =
+                new EnumMap<PmdTag, Method>(PmdTag.class);
+
+        for(Method method : filtMethod(klass, OpenXmlMark.class)){
+            Annotation anno = method.getAnnotation(OpenXmlMark.class);
+            OpenXmlMark mark = (OpenXmlMark) anno;
+            PmdTag tag = mark.value();
+            result.put(tag, method);
+        }
+
+        return result;
+    }
+
+    /**
+     * 注釈でマークされた終了タグ通知用ディスパッチテーブルを返す。
+     * @param klass 対象クラス
+     * @return ディスパッチテーブル
+     */
+    private static Map<PmdTag, Method> getCloseDispatcher(Class<?> klass){
+        Map<PmdTag, Method> result =
+                new EnumMap<PmdTag, Method>(PmdTag.class);
+
+        for(Method method : filtMethod(klass, CloseXmlMark.class)){
+            Annotation anno = method.getAnnotation(CloseXmlMark.class);
+            CloseXmlMark mark = (CloseXmlMark) anno;
+            PmdTag tag = mark.value();
+            result.put(tag, method);
+        }
+
+        return result;
+    }
+
+
+    /**
+     * ディスパッチテーブルに従いディスパッチする。
+     * @param map ディスパッチテーブル
+     * @param tag タグ種
+     * @return ディスパッチが行われなければfalse
+     */
+    private boolean dispatch(Map<PmdTag, Method> map, PmdTag tag){
+        Method method = map.get(tag);
+        if(method == null) return false;
+
+        try{
+            method.invoke(this);
+        }catch(IllegalAccessException ex){
+            assert false;
+        }catch(InvocationTargetException ex){
+            Throwable cause = ex.getTargetException();
+            if(cause instanceof RuntimeException){
+                throw (RuntimeException) cause;
+            }else if(cause instanceof Error){
+                throw (Error) cause;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * 開始タグ登場を通知する。
+     * @param tag タグ種別
+     * @param attr 属性群
+     * @return ディスパッチが行われなければfalse
+     */
+    boolean openDispatch(PmdTag tag, Attributes attr){
+        this.currentAttribute = attr;
+        return dispatch(this.openDispatcher, tag);
+    }
+
+    /**
+     * 終了タグ登場を通知する。
+     * @param tag タグ種別
+     * @return ディスパッチが行われなければfalse
+     */
+    boolean closeDispatch(PmdTag tag){
+        return dispatch(this.closeDispatcher, tag);
+    }
+
+    /**
+     * CharData出現の通知。
+     * @param ch 文字配列
+     * @param start 開始位置
+     * @param length 長さ
+     */
+    void addCharData(char[] ch, int start, int length){
+        return;
+    }
+
+    /**
+     * ビルド対象オブジェクトの登録。
+     * @param model ビルド対象オブジェクト
+     * @throws NullPointerException 引数がnull
+     */
+    void setPmdModel(PmdModel model) throws NullPointerException{
+        if(model == null) throw new NullPointerException();
+        this.pmdModel = model;
+        return;
+    }
+
+    /**
+     * ビルド対象オブジェクトの取得。
+     * @return ビルド対象オブジェクト。未登録の場合はnull。
+     */
+    protected PmdModel getPmdModel(){
+        return this.pmdModel;
+    }
+
+    /**
+     * xsd:string型属性値の読み込み。
+     * @param attr 属性名
+     * @return 属性値。該当する属性が無ければnull。
+     * @see "http://www.w3.org/TR/xmlschema-2/#string"
+     */
+    protected String getStringAttr(PmdAttr attr){
+        String attrName = attr.attr();
+        String result = this.currentAttribute.getValue(attrName);
+        return result;
+    }
+
+    /**
+     * xsd:boolean型属性値の読み込み。
+     * @param attr 属性名
+     * @return 属性値。
+     * @throws IllegalArgumentException boolean型表記ではない
+     * @see "http://www.w3.org/TR/xmlschema-2/#boolean"
+     */
+    protected boolean getBooleanAttr(PmdAttr attr)
+            throws IllegalArgumentException{
+        String attrName = attr.attr();
+        String attrVal = this.currentAttribute.getValue(attrName);
+        boolean bVal;
+        bVal = DatatypeConverter.parseBoolean(attrVal);
+        return bVal;
+    }
+
+    /**
+     * xsd:float型属性値の読み込み。
+     * @param attr 属性名
+     * @return 属性値。
+     * @throws NumberFormatException float型表記ではない
+     * @see "http://www.w3.org/TR/xmlschema-2/#float"
+     */
+    protected float getFloatAttr(PmdAttr attr)
+            throws NumberFormatException {
+        String attrName = attr.attr();
+        String attrVal = this.currentAttribute.getValue(attrName);
+        float fVal;
+        fVal = DatatypeConverter.parseFloat(attrVal);
+        return fVal;
+    }
+
+    /**
+     * xsd:int型属性値の読み込み。
+     * @param attr 属性名
+     * @return 属性値。
+     * @throws NumberFormatException int型表記ではない
+     * @see "http://www.w3.org/TR/xmlschema-2/#int"
+     */
+    protected int getIntAttr(PmdAttr attr)
+            throws NumberFormatException {
+        String attrName = attr.attr();
+        String attrVal = this.currentAttribute.getValue(attrName);
+        int iVal;
+        iVal = DatatypeConverter.parseInt(attrVal);
+        return iVal;
+    }
+
+}
diff --git a/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/SaxMaterialListener.java b/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/SaxMaterialListener.java
new file mode 100644 (file)
index 0000000..c8745ee
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * material listener from XML
+ *
+ * License : The MIT License
+ * Copyright(c) 2013 MikuToga Partners
+ */
+
+package jp.sfjp.mikutoga.pmd.model.xml;
+
+import java.awt.Color;
+import java.util.List;
+import jp.sfjp.mikutoga.corelib.I18nText;
+import jp.sfjp.mikutoga.pmd.model.Material;
+import jp.sfjp.mikutoga.pmd.model.ShadeInfo;
+import jp.sfjp.mikutoga.pmd.model.ToonMap;
+
+/*
+    + materialList
+        + material
+            + i18nName
+            + diffuse
+            + specular
+            + ambient
+            + toon
+            + textureFile
+            + spheremapFile
+    + toonMap
+        + toonDef
+*/
+
+/**
+ * マテリアル関連のXML要素出現イベントを受信する。
+ */
+class SaxMaterialListener extends SaxListener{
+
+    private static final char BS_CHAR  = (char) 0x005c;
+    private static final char YEN_CHAR = (char) 0x00a5;
+    private static final String BS_TXT  = Character.toString(BS_CHAR);
+    private static final String YEN_TXT = Character.toString(YEN_CHAR);
+
+    private static final int TOON_IDX_NONE = 255;
+
+
+    private final RefHelper helper;
+
+    private Material currentMaterial = null;
+
+
+    /**
+     * コンストラクタ。
+     * @param helper 参照ヘルパ
+     */
+    SaxMaterialListener(RefHelper helper) {
+        super();
+        this.helper = helper;
+        return;
+    }
+
+
+    /**
+     * 日本語Windows用ファイル名の正規化を行う。
+     * 文字U+00A5は文字U-005Cに変換される。
+     * @param txt 変換元文字列
+     * @return 正規化された文字列
+     */
+    private static String xferBslash(String txt){
+        String result;
+        result = txt.replace(YEN_TXT, BS_TXT);
+        return result;
+    }
+
+
+    /**
+     * materialタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.MATERIAL)
+    void openMaterial(){
+        this.currentMaterial = new Material();
+
+        String name = getStringAttr(PmdAttr.NAME);
+        boolean showEdge = getBooleanAttr(PmdAttr.SHOW_EDGE);
+        String surfaceGroupIdRef =
+                getStringAttr(PmdAttr.SURFACE_GROUP_IDREF);
+
+        I18nText i18nName = this.currentMaterial.getMaterialName();
+        if(name != null){
+            i18nName.setPrimaryText(name);
+        }
+
+        this.currentMaterial.setEdgeAppearance(showEdge);
+
+        this.currentMaterial.getShadeInfo().setToonIndex(TOON_IDX_NONE);
+
+        this.helper.addSurfaceGroupIdRef(this.currentMaterial,
+                                         surfaceGroupIdRef);
+
+        return;
+    }
+
+    /**
+     * materialタグ終了の通知を受け取る。
+     */
+    @CloseXmlMark(PmdTag.MATERIAL)
+    void closeMaterial(){
+        List<Material> materialList = getPmdModel().getMaterialList();
+        materialList.add(this.currentMaterial);
+        this.currentMaterial = null;
+
+        return;
+    }
+
+    /**
+     * i18nTextタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.I18N_NAME)
+    void openI18nText(){
+        String lang = getStringAttr(PmdAttr.LANG);
+        String name = getStringAttr(PmdAttr.NAME);
+
+        I18nText materialName = this.currentMaterial.getMaterialName();
+        materialName.setI18nText(lang, name);
+
+        return;
+    }
+
+    /**
+     * diffuseタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.DIFFUSE)
+    void openDiffuse(){
+        float rCol = getFloatAttr(PmdAttr.R);
+        float gCol = getFloatAttr(PmdAttr.G);
+        float bCol = getFloatAttr(PmdAttr.B);
+        float alpha = getFloatAttr(PmdAttr.ALPHA);
+
+        Color color = new Color(rCol, gCol, bCol, alpha);
+
+        this.currentMaterial.setDiffuseColor(color);
+
+        return;
+    }
+
+    /**
+     * specularタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.SPECULAR)
+    void openSpecular(){
+        float rCol = getFloatAttr(PmdAttr.R);
+        float gCol = getFloatAttr(PmdAttr.G);
+        float bCol = getFloatAttr(PmdAttr.B);
+
+        Color color = new Color(rCol, gCol, bCol);
+        float shine = getFloatAttr(PmdAttr.SHININESS);
+
+        this.currentMaterial.setSpecularColor(color);
+        this.currentMaterial.setShininess(shine);
+
+        return;
+    }
+
+    /**
+     * ambientタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.AMBIENT)
+    void openAmbient(){
+        float rCol = getFloatAttr(PmdAttr.R);
+        float gCol = getFloatAttr(PmdAttr.G);
+        float bCol = getFloatAttr(PmdAttr.B);
+
+        Color color = new Color(rCol, gCol, bCol);
+
+        this.currentMaterial.setAmbientColor(color);
+
+        return;
+    }
+
+    /**
+     * toonタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.TOON)
+    void openToon(){
+        String toonFileIdRef = getStringAttr(PmdAttr.TOONFILE_IDREF);
+
+        this.helper.addToonFileIdRef(this.currentMaterial, toonFileIdRef);
+
+        return;
+    }
+
+    /**
+     * textureFileタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.TEXTURE_FILE)
+    void openTextureFile(){
+        String fileName = getStringAttr(PmdAttr.WINFILE_NAME);
+        fileName = xferBslash(fileName);
+
+        ShadeInfo shadeInfo = this.currentMaterial.getShadeInfo();
+        shadeInfo.setTextureFileName(fileName);
+
+        return;
+    }
+
+    /**
+     * spheremapFileタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.SPHEREMAP_FILE)
+    void openSphemapFile(){
+        String fileName = getStringAttr(PmdAttr.WINFILE_NAME);
+        fileName = xferBslash(fileName);
+
+        ShadeInfo shadeInfo = this.currentMaterial.getShadeInfo();
+        shadeInfo.setSpheremapFileName(fileName);
+
+        return;
+    }
+
+    /**
+     * toonDefタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.TOON_DEF)
+    void openToonDef(){
+        String toonFileId = getStringAttr(PmdAttr.TOONFILE_ID);
+        int index         = getIntAttr(PmdAttr.INDEX);
+        String fileName   = getStringAttr(PmdAttr.WINFILE_NAME);
+        fileName = xferBslash(fileName);
+
+        ToonMap toonMap = getPmdModel().getToonMap();
+        toonMap.setIndexedToon(index, fileName);
+
+        this.helper.addToonIdx(toonFileId, index);
+
+        return;
+    }
+
+    /**
+     * toonMapタグ終了の通知を受け取る。
+     */
+    @CloseXmlMark(PmdTag.TOON_MAP)
+    void closeToonMap(){
+        this.helper.resolveToonIdx();
+        return;
+    }
+
+}
diff --git a/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/SaxModelListener.java b/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/SaxModelListener.java
new file mode 100644 (file)
index 0000000..99432e0
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * model listener from XML
+ *
+ * License : The MIT License
+ * Copyright(c) 2013 MikuToga Partners
+ */
+
+package jp.sfjp.mikutoga.pmd.model.xml;
+
+import jp.sfjp.mikutoga.corelib.I18nText;
+
+/*
+    + pmdModel
+        + i18nName
+        + description
+        + license
+        + credits
+        + meta
+        .....
+*/
+
+/**
+ * モデル関連のXML要素出現イベントを受信する。
+ */
+class SaxModelListener extends SaxListener{
+
+    private String currentLang = null;
+    private StringBuilder currentBredTxt = null;
+
+
+    /**
+     * コンストラクタ。
+     */
+    SaxModelListener() {
+        super();
+        return;
+    }
+
+
+    /**
+     * CharData出現の通知。
+     * @param ch 文字配列
+     * @param start 開始位置
+     * @param length 長さ
+     */
+    @Override
+    void addCharData(char[] ch, int start, int length){
+        if(this.currentBredTxt == null) return;
+
+        for(int idx = 0; idx < length; idx++){
+            int pos = start + idx;
+            char chData = ch[pos];
+            if(chData == '\n') continue;
+            if(chData == '\r') continue;
+            this.currentBredTxt.append(chData);
+        }
+
+        return;
+    }
+
+    /**
+     * pmdModelタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.PMD_MODEL)
+    void openPmdModel(){
+        I18nText modelName = getPmdModel().getModelName();
+
+        String name = getStringAttr(PmdAttr.NAME);
+        modelName.setPrimaryText(name);
+
+        return;
+    }
+
+    /**
+     * i18nTextタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.I18N_NAME)
+    void openI18nText(){
+        String lang = getStringAttr(PmdAttr.LANG);
+        String name = getStringAttr(PmdAttr.NAME);
+
+        I18nText modelName = getPmdModel().getModelName();
+        modelName.setI18nText(lang, name);
+
+        return;
+    }
+
+    /**
+     * descriptionタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.DESCRIPTION)
+    void openDescription(){
+        this.currentLang = getStringAttr(PmdAttr.LANG);
+        this.currentBredTxt = new StringBuilder();
+        return;
+    }
+
+    /**
+     * descriptionタグ終了の通知を受け取る。
+     */
+    @CloseXmlMark(PmdTag.DESCRIPTION)
+    void closeDescription(){
+        String bredText = this.currentBredTxt.toString();
+
+        I18nText desc = getPmdModel().getDescription();
+        if(this.currentLang == null){
+            desc.setPrimaryText(bredText);
+        }else{
+            desc.setI18nText(this.currentLang, bredText);
+        }
+
+        this.currentLang = null;
+        this.currentBredTxt = null;
+
+        return;
+    }
+
+    /**
+     * brタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.BR)
+    void openBr(){
+        if(this.currentBredTxt == null) return;
+        this.currentBredTxt.append('\n');
+        return;
+    }
+
+}
diff --git a/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/SaxMorphListener.java b/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/SaxMorphListener.java
new file mode 100644 (file)
index 0000000..c118b28
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * morph listener from XML
+ *
+ * License : The MIT License
+ * Copyright(c) 2013 MikuToga Partners
+ */
+
+package jp.sfjp.mikutoga.pmd.model.xml;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import jp.sfjp.mikutoga.corelib.I18nText;
+import jp.sfjp.mikutoga.math.MkPos3D;
+import jp.sfjp.mikutoga.pmd.MorphType;
+import jp.sfjp.mikutoga.pmd.model.ListUtil;
+import jp.sfjp.mikutoga.pmd.model.MorphPart;
+import jp.sfjp.mikutoga.pmd.model.MorphVertex;
+
+/*
+    + morphList
+        + morph
+            + i18nName
+            + morphVertex
+*/
+
+/**
+ * モーフ関連のXML要素出現イベントを受信する。
+ */
+class SaxMorphListener extends SaxListener{
+
+    private final RefHelper helper;
+
+    private MorphPart currentMorph = null;
+
+
+    /**
+     * コンストラクタ。
+     * @param helper 参照ヘルパ
+     */
+    SaxMorphListener(RefHelper helper) {
+        super();
+        this.helper = helper;
+        return;
+    }
+
+
+    /**
+     * morphListタグ終了の通知を受け取る。
+     * 各モーフは0番ではなく1番から採番される。
+     * 0番は暗黙のBASEモーフ。
+     */
+    @CloseXmlMark(PmdTag.MORPH_LIST)
+    void closeMorphList(){
+        Map<MorphType, List<MorphPart>> morphMap =
+                getPmdModel().getMorphMap();
+        List<MorphPart> tempList = new LinkedList<MorphPart>();
+
+        tempList.addAll(morphMap.get(MorphType.EYEBROW));
+        tempList.addAll(morphMap.get(MorphType.EYE));
+        tempList.addAll(morphMap.get(MorphType.LIP));
+        tempList.addAll(morphMap.get(MorphType.EXTRA));
+
+        MorphPart baseDummy = new MorphPart();
+        tempList.add(0, baseDummy);  // BASE dummy
+
+        ListUtil.assignIndexedSerial(tempList);
+
+        return;
+    }
+
+    /**
+     * morphタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.MORPH)
+    void openMorph(){
+        this.currentMorph = new MorphPart();
+
+        String name = getStringAttr(PmdAttr.NAME);
+        I18nText morphName = this.currentMorph.getMorphName();
+        morphName.setPrimaryText(name);
+
+        String type = getStringAttr(PmdAttr.TYPE);
+        MorphType morphType = MorphType.valueOf(type);
+        this.currentMorph.setMorphType(morphType);
+
+        return;
+    }
+
+    /**
+     * morphタグ終了の通知を受け取る。
+     */
+    @CloseXmlMark(PmdTag.MORPH)
+    void closeMorph(){
+        Map<MorphType, List<MorphPart>> morphMap =
+                getPmdModel().getMorphMap();
+
+        MorphType morphType = this.currentMorph.getMorphType();
+        List<MorphPart> morphList = morphMap.get(morphType);
+        morphList.add(this.currentMorph);
+
+        this.currentMorph = null;
+
+        return;
+    }
+
+    /**
+     * i18nTextタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.I18N_NAME)
+    void openI18nText(){
+        String lang = getStringAttr(PmdAttr.LANG);
+        String name = getStringAttr(PmdAttr.NAME);
+
+        I18nText morphName = this.currentMorph.getMorphName();
+        morphName.setI18nText(lang, name);
+
+        return;
+    }
+
+    /**
+     * morphVertexタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.MORPH_VERTEX)
+    void openMorphVertex(){
+        MorphVertex morphVertex = new MorphVertex();
+
+        String vertexIdRef = getStringAttr(PmdAttr.VERTEX_IDREF);
+        this.helper.addMorphVertexIdRef(morphVertex, vertexIdRef);
+
+        float xOff = getFloatAttr(PmdAttr.XOFF);
+        float yOff = getFloatAttr(PmdAttr.YOFF);
+        float zOff = getFloatAttr(PmdAttr.ZOFF);
+        MkPos3D offset = morphVertex.getOffset();
+        offset.setPosition(xOff, yOff, zOff);
+
+        List<MorphVertex> morphVertexList =
+                this.currentMorph.getMorphVertexList();
+        morphVertexList.add(morphVertex);
+
+        return;
+    }
+
+}
diff --git a/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/SaxShapeListener.java b/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/SaxShapeListener.java
new file mode 100644 (file)
index 0000000..fbf689d
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ * shape listener from XML
+ *
+ * License : The MIT License
+ * Copyright(c) 2013 MikuToga Partners
+ */
+
+package jp.sfjp.mikutoga.pmd.model.xml;
+
+import java.util.List;
+import jp.sfjp.mikutoga.math.MkPos2D;
+import jp.sfjp.mikutoga.math.MkPos3D;
+import jp.sfjp.mikutoga.math.MkVec3D;
+import jp.sfjp.mikutoga.pmd.model.BoneInfo;
+import jp.sfjp.mikutoga.pmd.model.ListUtil;
+import jp.sfjp.mikutoga.pmd.model.Surface;
+import jp.sfjp.mikutoga.pmd.model.Vertex;
+
+/*
+    + surfaceGroupList
+        + surfaceGroup
+            + surface
+    + vertexList
+        + vertex
+            + position
+            + normal
+            + uvMap
+            + skinning
+*/
+
+/**
+ * 形状関連のXML要素出現イベントを受信する。
+ */
+class SaxShapeListener extends SaxListener{
+
+    private final RefHelper helper;
+
+    private String currentSurfaceGroupId = null;
+    private Vertex currentVertex = null;
+
+
+    /**
+     * コンストラクタ。
+     * @param helper 参照ヘルパ
+     */
+    SaxShapeListener(RefHelper helper) {
+        super();
+        this.helper = helper;
+        return;
+    }
+
+
+    /**
+     * surfaceGroupListタグ終了の通知を受け取る。
+     */
+    @CloseXmlMark(PmdTag.SURFACE_GROUP_LIST)
+    void closeSurfaceGroupList(){
+        this.helper.resolveMaterialSurfaceGroupId();
+        return;
+    }
+
+    /**
+     * surfaceGroupタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.SURFACE_GROUP)
+    void openSurfaceGroup(){
+        String surfaceGroupId = getStringAttr(PmdAttr.SURFACE_GROUP_ID);
+        this.currentSurfaceGroupId = surfaceGroupId;
+
+        return;
+    }
+
+    /**
+     * surfaceGroupタグ終了の通知を受け取る。
+     */
+    @CloseXmlMark(PmdTag.SURFACE_GROUP)
+    void closeSurfaceGroup(){
+        this.currentSurfaceGroupId = null;
+        return;
+    }
+
+    /**
+     * surfaceタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.SURFACE)
+    void openSurface(){
+        Surface surface = new Surface();
+
+        String vtxIdRef1 = getStringAttr(PmdAttr.VERTEX_IDREF_1);
+        String vtxIdRef2 = getStringAttr(PmdAttr.VERTEX_IDREF_2);
+        String vtxIdRef3 = getStringAttr(PmdAttr.VERTEX_IDREF_3);
+
+        this.helper.addSurfaceGroup(this.currentSurfaceGroupId, surface);
+        this.helper.addSurfaceVertex(surface,
+                                     vtxIdRef1,
+                                     vtxIdRef2,
+                                     vtxIdRef3 );
+
+        List<Surface> surfaceList = getPmdModel().getSurfaceList();
+        surfaceList.add(surface);
+
+        return;
+    }
+
+    /**
+     * vertexタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.VERTEX)
+    void openVertex(){
+        this.currentVertex = new Vertex();
+
+        String vtxId     = getStringAttr(PmdAttr.VERTEX_ID);
+        boolean showEdge = getBooleanAttr(PmdAttr.SHOW_EDGE);
+
+        this.currentVertex.setEdgeAppearance(showEdge);
+
+        this.helper.addVertexId(vtxId, this.currentVertex);
+
+        return;
+    }
+
+    /**
+     * vertexタグ終了の通知を受け取る。
+     */
+    @CloseXmlMark(PmdTag.VERTEX)
+    void closeVertex(){
+        List<Vertex> vertexList = getPmdModel().getVertexList();
+
+        vertexList.add(this.currentVertex);
+
+        this.currentVertex = null;
+
+        return;
+    }
+
+    /**
+     * positionタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.POSITION)
+    void openPosition(){
+        float x = getFloatAttr(PmdAttr.X);
+        float y = getFloatAttr(PmdAttr.Y);
+        float z = getFloatAttr(PmdAttr.Z);
+
+        MkPos3D pos = this.currentVertex.getPosition();
+        pos.setPosition(x, y, z);
+
+        return;
+    }
+
+    /**
+     * normalタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.NORMAL)
+    void openNormal(){
+        float x = getFloatAttr(PmdAttr.X);
+        float y = getFloatAttr(PmdAttr.Y);
+        float z = getFloatAttr(PmdAttr.Z);
+
+        MkVec3D normal = this.currentVertex.getNormal();
+        normal.setVector(x, y, z);
+
+        return;
+    }
+
+    /**
+     * uvMapタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.UV_MAP)
+    void openUvMap(){
+        float u = getFloatAttr(PmdAttr.U);
+        float v = getFloatAttr(PmdAttr.V);
+
+        MkPos2D pos = this.currentVertex.getUVPosition();
+        pos.setPosition(u, v);
+
+        return;
+    }
+
+    /**
+     * skinningタグ開始の通知を受け取る。
+     */
+    @OpenXmlMark(PmdTag.SKINNING)
+    void openSkinning(){
+        String boneIdRef1 = getStringAttr(PmdAttr.BONE_IDREF_1);
+        String boneIdRef2 = getStringAttr(PmdAttr.BONE_IDREF_2);
+        BoneInfo bone1 = this.helper.findBoneId(boneIdRef1);
+        BoneInfo bone2 = this.helper.findBoneId(boneIdRef2);
+
+        int weightBalance = getIntAttr(PmdAttr.WEIGHT_BALANCE);
+
+        this.currentVertex.setBonePair(bone1, bone2);
+        this.currentVertex.setWeightA(weightBalance);
+
+        return;
+    }
+
+    /**
+     * vertexListタグ終了の通知を受け取る。
+     */
+    @CloseXmlMark(PmdTag.VERTEX_LIST)
+    void closeVertexList(){
+        this.helper.resolveMorphVertexIdRef();
+        this.helper.resolveSurfaceVertexIdRef();
+
+        List<Vertex> vertexList = getPmdModel().getVertexList();
+        ListUtil.assignIndexedSerial(vertexList);
+
+        return;
+    }
+
+}
@@ -5,11 +5,11 @@
  * Copyright(c) 2010 MikuToga Partners
  */
 
-package jp.sfjp.mikutoga.pmd.xml;
+package jp.sfjp.mikutoga.pmd.model.xml;
 
 import java.net.URI;
 import java.net.URISyntaxException;
-import jp.sourceforge.mikutoga.xml.LocalXmlResource;
+import jp.sfjp.mikutoga.xml.LocalXmlResource;
 
 /**
  * 101009形式XML各種リソースの定義。
@@ -5,11 +5,11 @@
  * Copyright(c) 2013 MikuToga Partners
  */
 
-package jp.sfjp.mikutoga.pmd.xml;
+package jp.sfjp.mikutoga.pmd.model.xml;
 
 import java.net.URI;
 import java.net.URISyntaxException;
-import jp.sourceforge.mikutoga.xml.LocalXmlResource;
+import jp.sfjp.mikutoga.xml.LocalXmlResource;
 
 /**
  * 130128形式XML各種リソースの定義。
diff --git a/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/XmlHandler.java b/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/XmlHandler.java
new file mode 100644 (file)
index 0000000..f8db09c
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * xml to pmd SAX Handler
+ *
+ * License : The MIT License
+ * Copyright(c) 2013 MikuToga Partners
+ */
+
+package jp.sfjp.mikutoga.pmd.model.xml;
+
+import java.util.EnumMap;
+import java.util.Map;
+import jp.sfjp.mikutoga.pmd.model.PmdModel;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+
+/**
+ * XMLモデルファイルパース用SAXハンドラ。
+ * <p>下位リスナに各種通知が振り分けられる。
+ */
+class XmlHandler implements ContentHandler{
+
+    private final Map<PmdTag, SaxListener> listenerMap;
+    private SaxListener currentListener = null;
+
+    private PmdModel pmdModel = null;
+
+    private String nspfx = "";
+    private String nsuri = null;
+
+
+    /**
+     * コンストラクタ。
+     */
+    XmlHandler(){
+        super();
+
+        RefHelper helper = new RefHelper();
+        SaxListener modelListener    = new SaxModelListener();
+        SaxListener materialListener = new SaxMaterialListener(helper);
+        SaxListener boneListener     = new SaxBoneListener(helper);
+        SaxListener morphListener    = new SaxMorphListener(helper);
+        SaxListener dynamicsListener = new SaxDynamicsListener(helper);
+        SaxListener shapeListener    = new SaxShapeListener(helper);
+
+        this.listenerMap = new EnumMap<PmdTag, SaxListener>(PmdTag.class);
+        this.listenerMap.put(PmdTag.PMD_MODEL,          modelListener);
+        this.listenerMap.put(PmdTag.MATERIAL_LIST,      materialListener);
+        this.listenerMap.put(PmdTag.BONE_LIST,          boneListener);
+        this.listenerMap.put(PmdTag.MORPH_LIST,         morphListener);
+        this.listenerMap.put(PmdTag.RIGID_LIST,         dynamicsListener);
+        this.listenerMap.put(PmdTag.SURFACE_GROUP_LIST, shapeListener);
+
+        return;
+    }
+
+
+    /**
+     * ビルド対象のモデルを返す。
+     * @return ビルド対象のモデル。ビルド前ならnull
+     */
+    PmdModel getPmdModel(){
+        return this.pmdModel;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws SAXException {@inheritDoc}
+     */
+    @Override
+    public void startDocument() throws SAXException{
+        this.pmdModel = new PmdModel();
+
+        for(SaxListener listener : this.listenerMap.values()){
+            listener.setPmdModel(this.pmdModel);
+        }
+
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws SAXException {@inheritDoc}
+     */
+    @Override
+    public void endDocument() throws SAXException{
+        assert this.pmdModel != null;
+        this.currentListener = null;
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param prefix {@inheritDoc}
+     * @param uri {@inheritDoc}
+     * @throws SAXException {@inheritDoc}
+     */
+    @Override
+    public void startPrefixMapping(String prefix, String uri)
+            throws SAXException {
+        if(   Schema101009.NS_PMDXML.equals(uri)
+           || Schema130128.NS_PMDXML.equals(uri) ){
+            this.nspfx = prefix;
+            this.nsuri = uri;
+        }
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param prefix {@inheritDoc}
+     * @throws SAXException {@inheritDoc}
+     */
+    @Override
+    public void endPrefixMapping(String prefix) throws SAXException {
+        if(prefix.equals(this.nspfx)){
+            this.nspfx = "";
+            this.nsuri = null;
+        }
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param uri {@inheritDoc}
+     * @param localName {@inheritDoc}
+     * @param qName {@inheritDoc}
+     * @param attr {@inheritDoc}
+     * @throws SAXException {@inheritDoc}
+     */
+    @Override
+    public void startElement(String uri,
+                               String localName,
+                               String qName,
+                               Attributes attr)
+            throws SAXException {
+        if( ! this.nsuri.equals(uri) ) return;
+
+        PmdTag tag = PmdTag.parse(localName);
+        if(tag == null) return;
+
+        switchListener(tag);
+
+        if(this.currentListener == null) return;
+        this.currentListener.openDispatch(tag, attr);
+
+        return;
+    }
+
+    /**
+     * タグ出現に従い通知リスナを切り替える。
+     * @param tag タグ種別
+     */
+    private void switchListener(PmdTag tag){
+        SaxListener newListener = this.listenerMap.get(tag);
+        if(newListener == null) return;
+
+        this.currentListener = newListener;
+
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param uri {@inheritDoc}
+     * @param localName {@inheritDoc}
+     * @param qName {@inheritDoc}
+     * @throws SAXException {@inheritDoc}
+     */
+    @Override
+    public void endElement(String uri, String localName, String qName)
+            throws SAXException {
+        if( ! this.nsuri.equals(uri) ) return;
+
+        PmdTag tag = PmdTag.parse(localName);
+        if(tag == null) return;
+
+        if(this.currentListener != null){
+            this.currentListener.closeDispatch(tag);
+        }
+
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param locator {@inheritDoc}
+     */
+    @Override
+    public void setDocumentLocator(Locator locator){
+        // NOTHING
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param target {@inheritDoc}
+     * @param data {@inheritDoc}
+     * @throws SAXException {@inheritDoc}
+     */
+    @Override
+    public void processingInstruction(String target, String data)
+            throws SAXException {
+        // NOTHING
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param ch {@inheritDoc}
+     * @param start {@inheritDoc}
+     * @param length {@inheritDoc}
+     * @throws SAXException {@inheritDoc}
+     */
+    @Override
+    public void characters(char[] ch, int start, int length)
+            throws SAXException {
+        if(this.currentListener == null) return;
+        this.currentListener.addCharData(ch, start, length);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param ch {@inheritDoc}
+     * @param start {@inheritDoc}
+     * @param length {@inheritDoc}
+     * @throws SAXException {@inheritDoc}
+     */
+    @Override
+    public void ignorableWhitespace(char[] ch, int start, int length)
+            throws SAXException {
+        // NOTHING
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param name {@inheritDoc}
+     * @throws SAXException {@inheritDoc}
+     */
+    @Override
+    public void skippedEntity(String name) throws SAXException{
+        // NOTHING
+        return;
+    }
+
+}
@@ -5,7 +5,7 @@
  * Copyright(c) 2013 MikuToga Partners
  */
 
-package jp.sfjp.mikutoga.pmd.xml;
+package jp.sfjp.mikutoga.pmd.model.xml;
 
 /**
  * XMLファイルスキーマ種別。
diff --git a/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/XmlPmdLoader.java b/src/main/java/jp/sfjp/mikutoga/pmd/model/xml/XmlPmdLoader.java
new file mode 100644 (file)
index 0000000..c7a3411
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * xml to pmd loader
+ *
+ * License : The MIT License
+ * Copyright(c) 2013 MikuToga Partners
+ */
+
+package jp.sfjp.mikutoga.pmd.model.xml;
+
+import java.io.IOException;
+import jp.sfjp.mikutoga.pmd.model.PmdModel;
+import jp.sfjp.mikutoga.xml.TogaXmlException;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+
+/**
+ * XMLモデルファイルを読み込むためのローダ。
+ */
+public class XmlPmdLoader {
+
+    private static final String SAXFEATURES_NAMESPACES =
+            "http://xml.org/sax/features/namespaces";
+
+    private final XMLReader reader;
+
+
+    /**
+     * コンストラクタ。
+     * <p>XMLリーダは名前空間をサポートしていなければならない。
+     * @param reader XMLリーダ
+     * @throws NullPointerException 引数がnull
+     * @throws SAXException 機能不足のXMLリーダが渡された
+     */
+    public XmlPmdLoader(XMLReader reader)
+            throws NullPointerException, SAXException {
+        super();
+
+        if(reader == null) throw new NullPointerException();
+        if( ! reader.getFeature(SAXFEATURES_NAMESPACES) ){
+            throw new SAXException();
+        }
+
+        this.reader = reader;
+
+        return;
+    }
+
+
+    /**
+     * XMLのパースを開始する。
+     * @param source XML入力
+     * @return モデルデータ
+     * @throws SAXException 構文エラー
+     * @throws IOException 入力エラー
+     * @throws TogaXmlException 構文エラー
+     */
+    public PmdModel parse(InputSource source)
+            throws SAXException, IOException, TogaXmlException{
+        XmlHandler saxHandler = new XmlHandler();
+        this.reader.setContentHandler(saxHandler);
+
+        try{
+            this.reader.parse(source);
+        }catch(SAXException e){
+            Throwable cause = e.getCause();
+            if(cause instanceof TogaXmlException){
+                throw (TogaXmlException) cause;
+            }
+            throw e;
+        }
+
+        return saxHandler.getPmdModel();
+    }
+
+}
@@ -6,9 +6,9 @@
  */
 
 /**
- * PMDモデル内容をXMLで出力するためのライブラリ。
+ * PMDã\83¢ã\83\87ã\83«å\86\85容ã\82\92XMLã\81§å\85¥å\87ºå\8a\9bã\81\99ã\82\8bã\81\9fã\82\81ã\81®ã\83©ã\82¤ã\83\96ã\83©ã\83ªã\80\82
  */
 
-package jp.sfjp.mikutoga.pmd.xml;
+package jp.sfjp.mikutoga.pmd.model.xml;
 
 /* EOF */
diff --git a/src/main/java/jp/sfjp/mikutoga/pmd/xml/PmdXmlExporter.java b/src/main/java/jp/sfjp/mikutoga/pmd/xml/PmdXmlExporter.java
deleted file mode 100644 (file)
index e149129..0000000
+++ /dev/null
@@ -1,1425 +0,0 @@
-/*
- * pmd-xml exporter
- *
- * License : The MIT License
- * Copyright(c) 2010 MikuToga Partners
- */
-
-package jp.sfjp.mikutoga.pmd.xml;
-
-import java.awt.Color;
-import java.io.IOException;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import jp.sfjp.mikutoga.corelib.I18nText;
-import jp.sfjp.mikutoga.math.MkPos2D;
-import jp.sfjp.mikutoga.math.MkPos3D;
-import jp.sfjp.mikutoga.math.MkVec3D;
-import jp.sfjp.mikutoga.pmd.BoneType;
-import jp.sfjp.mikutoga.pmd.Deg3d;
-import jp.sfjp.mikutoga.pmd.MorphType;
-import jp.sfjp.mikutoga.pmd.Rad3d;
-import jp.sfjp.mikutoga.pmd.RigidShapeType;
-import jp.sfjp.mikutoga.pmd.TripletRange;
-import jp.sfjp.mikutoga.pmd.model.BoneGroup;
-import jp.sfjp.mikutoga.pmd.model.BoneInfo;
-import jp.sfjp.mikutoga.pmd.model.DynamicsInfo;
-import jp.sfjp.mikutoga.pmd.model.IKChain;
-import jp.sfjp.mikutoga.pmd.model.JointInfo;
-import jp.sfjp.mikutoga.pmd.model.Material;
-import jp.sfjp.mikutoga.pmd.model.MorphPart;
-import jp.sfjp.mikutoga.pmd.model.MorphVertex;
-import jp.sfjp.mikutoga.pmd.model.PmdModel;
-import jp.sfjp.mikutoga.pmd.model.RigidGroup;
-import jp.sfjp.mikutoga.pmd.model.RigidInfo;
-import jp.sfjp.mikutoga.pmd.model.RigidShape;
-import jp.sfjp.mikutoga.pmd.model.SerialNumbered;
-import jp.sfjp.mikutoga.pmd.model.ShadeInfo;
-import jp.sfjp.mikutoga.pmd.model.Surface;
-import jp.sfjp.mikutoga.pmd.model.ToonMap;
-import jp.sfjp.mikutoga.pmd.model.Vertex;
-import jp.sourceforge.mikutoga.xml.BasicXmlExporter;
-import jp.sourceforge.mikutoga.xml.XmlResourceResolver;
-
-/**
- * 101009形式XMLでPMDモデルデータを出力する。
- */
-public class PmdXmlExporter extends BasicXmlExporter{
-
-    private static final String TOP_COMMENT =
-              "  MikuMikuDance\n"
-            + "    model-data(*.pmd) on XML";
-
-    /** 改行文字列 CR。 */
-    private static final String CR = "\r";       // 0x0d
-    /** 改行文字列 LF。 */
-    private static final String LF = "\n";       // 0x0a
-    /** 改行文字列 CRLF。 */
-    private static final String CRLF = CR + LF;  // 0x0d, 0x0a
-
-    private static final String PFX_SURFACEGROUP = "sg";
-    private static final String PFX_TOONFILE = "tf";
-    private static final String PFX_VERTEX = "vtx";
-    private static final String PFX_BONE = "bn";
-    private static final String PFX_RIGID = "rd";
-    private static final String PFX_RIGIDGROUP = "rg";
-
-    private static final String BONETYPE_COMMENT =
-          "Bone types:\n"
-        + "[0 : ROTATE      : Rotate       : 回転           :]\n"
-        + "[1 : ROTMOV      : Rotate/Move  : 回転/移動      :]\n"
-        + "[2 : IK          : IK           : IK             :]\n"
-        + "[3 : UNKNOWN     : Unknown      : 不明           :]\n"
-        + "[4 : UNDERIK     : Under IK     : IK影響下(回転) :]\n"
-        + "[5 : UNDERROT    : Under rotate : 回転影響下     :]\n"
-        + "[6 : IKCONNECTED : IK connected : IK接続先       :]\n"
-        + "[7 : HIDDEN      : Hidden       : 非表示         :]\n"
-        + "[8 : TWIST       : Twist        : 捩り           :]\n"
-        + "[9 : LINKEDROT   : Linked Rotate: 回転連動       :]\n";
-
-    private static final String MORPHTYPE_COMMENT =
-          "Morph types:\n"
-        + "[1 : EYEBROW : まゆ   ]\n"
-        + "[2 : EYE     : 目     ]\n"
-        + "[3 : LIP     : リップ ]\n"
-        + "[4 : EXTRA   : その他 ]\n";
-
-    private static final String RIGIDBEHAVIOR_COMMENT =
-          "Rigid behavior types:\n"
-        + "[0 : FOLLOWBONE    : ボーン追従       ]\n"
-        + "[1 : ONLYDYNAMICS  : 物理演算         ]\n"
-        + "[2 : BONEDDYNAMICS : ボーン位置合わせ ]\n";
-
-    private static final Locale DEF_LOCALE = Locale.JAPANESE;
-
-
-    private String generator = null;
-
-    private XmlModelFileType xmlType = XmlModelFileType.XML_101009;
-
-
-    /**
-     * コンストラクタ。
-     */
-    public PmdXmlExporter(){
-        super();
-        return;
-    }
-
-
-    /**
-     * 出力XMLファイル種別を返す。
-     * @return ファイル種別
-     */
-    public XmlModelFileType getXmlFileType(){
-        return this.xmlType;
-    }
-
-    /**
-     * 出力XMLファイル種別を設定する。
-     * @param type ファイル種別
-     */
-    public void setXmlFileType(XmlModelFileType type){
-        switch(type){
-        case XML_101009:
-        case XML_130128:
-            this.xmlType = type;
-            break;
-        case XML_AUTO:
-            this.xmlType = XmlModelFileType.XML_130128;
-            break;
-        default:
-            throw new IllegalArgumentException();
-        }
-
-        assert this.xmlType == XmlModelFileType.XML_101009
-            || this.xmlType == XmlModelFileType.XML_130128;
-
-        return;
-    }
-
-    /**
-     * Generatorメタ情報を設定する。
-     * @param generatorArg Generatorメタ情報。表示したくないときはnull
-     */
-    public void setGenerator(String generatorArg){
-        this.generator = generatorArg;
-        return;
-    }
-
-    /**
-     * 任意の文字列がBasicLatin文字のみから構成されるか判定する。
-     * @param seq 文字列
-     * @return null、長さ0もしくはBasicLatin文字のみから構成されるならtrue
-     */
-    public static boolean hasOnlyBasicLatin(CharSequence seq){
-        if(seq == null) return true;
-        int length = seq.length();
-        for(int pos = 0; pos < length; pos++){
-            char ch = seq.charAt(pos);
-            if(ch > 0x007f) return false;
-        }
-        return true;
-    }
-
-    /**
-     * {@inheritDoc}
-     * @return {@inheritDoc}
-     * @throws IOException {@inheritDoc}
-     */
-    @Override
-    public PmdXmlExporter ind() throws IOException{
-        super.ind();
-        return this;
-    }
-
-    /**
-     * 文字参照によるエスケープを補佐するためのコメントを出力する。
-     * @param seq 文字列
-     * @return this本体
-     * @throws IOException 出力エラー
-     */
-    protected PmdXmlExporter putUnescapedComment(CharSequence seq)
-            throws IOException{
-        if( ! isBasicLatinOnlyOut() ) return this;
-        if(hasOnlyBasicLatin(seq)) return this;
-        sp().putLineComment(seq);
-        return this;
-    }
-
-    /**
-     * 多言語化された各種識別名を出力する。
-     * プライマリ名は出力対象外。
-     * @param text 多言語文字列
-     * @return this本体
-     * @throws IOException 出力エラー
-     */
-    protected PmdXmlExporter putI18nName(I18nText text) throws IOException{
-        for(String lang639 : text.lang639CodeList()){
-            if(lang639.equals(I18nText.CODE639_PRIMARY)) continue;
-            String name = text.getI18nText(lang639);
-            ind().putRawText("<i18nName ");
-            putAttr("lang", lang639).sp();
-            putAttr("name", name);
-            putRawText(" />");
-            putUnescapedComment(name);
-            ln();
-        }
-        return this;
-    }
-
-    /**
-     * 番号付けされたID(IDREF)属性を出力する。
-     * @param attrName 属性名
-     * @param prefix IDプレフィクス
-     * @param num 番号
-     * @return this本体
-     * @throws IOException 出力エラー
-     */
-    protected PmdXmlExporter putNumberedIdAttr(CharSequence attrName,
-                                                 CharSequence prefix,
-                                                 int num )
-            throws IOException{
-        putRawText(attrName).putRawText("=\"");
-        putRawText(prefix).putXsdInt(num);
-        putRawCh('"');
-        return this;
-    }
-
-    /**
-     * 番号付けされたID(IDREF)属性を出力する。
-     * @param attrName 属性名
-     * @param prefix IDプレフィクス
-     * @param numbered 番号付けされたオブジェクト
-     * @return this本体
-     * @throws IOException 出力エラー
-     */
-    protected PmdXmlExporter putNumberedIdAttr(CharSequence attrName,
-                                                 CharSequence prefix,
-                                                 SerialNumbered numbered )
-            throws IOException{
-        putNumberedIdAttr(attrName, prefix, numbered.getSerialNumber());
-        return this;
-    }
-
-    /**
-     * 位置情報を出力する。
-     * @param position 位置情報
-     * @return this本体
-     * @throws IOException 出力エラー
-     */
-    protected PmdXmlExporter putPosition(MkPos3D position)
-            throws IOException{
-        putRawText("<position ");
-        putFloatAttr("x", (float) position.getXpos()).sp();
-        putFloatAttr("y", (float) position.getYpos()).sp();
-        putFloatAttr("z", (float) position.getZpos()).sp();
-        putRawText("/>");
-        return this;
-    }
-
-    /**
-     * 姿勢情報(ラジアン)を出力する。
-     * @param rotation 姿勢情報
-     * @return this本体
-     * @throws IOException 出力エラー
-     */
-    protected PmdXmlExporter putRadRotation(Rad3d rotation)
-            throws IOException{
-        putRawText("<radRotation ");
-        putFloatAttr("xRad", rotation.getXRad()).sp();
-        putFloatAttr("yRad", rotation.getYRad()).sp();
-        putFloatAttr("zRad", rotation.getZRad()).sp();
-        putRawText("/>");
-        return this;
-    }
-
-    /**
-     * 多言語識別名属性のローカルな名前をコメント出力する。
-     * @param name 多言語識別名
-     * @return this本体
-     * @throws IOException 出力エラー
-     */
-    protected PmdXmlExporter putLocalNameComment(I18nText name)
-            throws IOException{
-        String localName = name.getText();
-        if(localName.isEmpty()){
-            localName = "[NAMELESS]";
-        }
-        ind().putLineComment(localName);
-        return this;
-    }
-
-    /**
-     * 多言語識別名属性のプライマリな名前を出力する。
-     * @param attrName 属性名
-     * @param name 多言語識別名
-     * @return this本体
-     * @throws IOException 出力エラー
-     */
-    protected PmdXmlExporter putPrimaryNameAttr(CharSequence attrName,
-                                                   I18nText name)
-            throws IOException{
-        String primaryName = name.getPrimaryText();
-        putAttr(attrName, primaryName);
-        return this;
-    }
-
-    /**
-     * PMDモデルデータをXML形式で出力する。
-     * @param model PMDモデルデータ
-     * @param xmlOut XML出力先
-     * @throws IOException 出力エラー
-     */
-    public void putPmdModel(PmdModel model, Appendable xmlOut)
-            throws IOException{
-        setAppendable(xmlOut);
-
-        ind().putRawText("<?xml")
-                .sp().putAttr("version","1.0")
-                .sp().putAttr("encoding","UTF-8")
-                .sp().putRawText("?>").ln(2);
-
-        ind().putBlockComment(TOP_COMMENT).ln(2);
-
-        I18nText modelName = model.getModelName();
-        ind().putLocalNameComment(modelName).ln();
-        ind().putRawText("<pmdModel").ln();
-        pushNest();
-
-        String defns;
-        String xsduri;
-        String version;
-        if(this.xmlType == XmlModelFileType.XML_101009){
-            defns   = Schema101009.NS_PMDXML;
-            xsduri  = Schema101009.SCHEMA_PMDXML;
-            version = Schema101009.VER_PMDXML;
-        }else if(this.xmlType == XmlModelFileType.XML_130128){
-            defns   = Schema130128.NS_PMDXML;
-            xsduri  = Schema130128.SCHEMA_PMDXML;
-            version = Schema130128.VER_PMDXML;
-        }else{
-            assert false;
-            throw new AssertionError();
-        }
-
-        ind().putAttr("xmlns", defns).ln();
-
-        ind().putAttr("xmlns:xsi", XmlResourceResolver.NS_XSD).ln();
-
-        ind().putRawText("xsi:schemaLocation").putRawText("=\"");
-        putRawText(defns).ln();
-        pushNest();
-        ind().putRawText(xsduri).putRawCh('"').ln();
-        popNest();
-
-        ind().putAttr("schemaVersion", version).ln(2);
-        ind().putPrimaryNameAttr("name", modelName).ln();
-
-        popNest();
-        putRawText(">").ln(2);
-
-        putModelInfo(model).flush();
-        putMetaInfo(model).flush();
-        putMaterialList(model).flush();
-        putToonMap(model).flush();
-        putBoneList(model).flush();
-        putBoneGroupList(model).flush();
-        putIKChainList(model).flush();
-        putMorphList(model).flush();
-        putRigidList(model).flush();
-        putRigidGroupList(model).flush();
-        putJointList(model).flush();
-        putSurfaceGroupList(model).flush();
-        putVertexList(model).flush();
-
-        ind().putRawText("</pmdModel>").ln(2);
-        ind().putRawText("<!-- EOF -->").ln();
-
-        return;
-    }
-
-    /**
-     * モデル基本情報を出力する。
-     * @param model モデル情報
-     * @return this本体
-     * @throws IOException 出力エラー
-     */
-    private PmdXmlExporter putModelInfo(PmdModel model)
-            throws IOException{
-        I18nText modelName = model.getModelName();
-        putI18nName(modelName);
-        ln();
-
-        I18nText description = model.getDescription();
-        for(String lang639 : description.lang639CodeList()){
-            String descText = description.getI18nText(lang639);
-            putDescription(lang639, descText);
-            ln();
-        }
-
-        return this;
-    }
-
-    /**
-     * モデル詳細テキストを出力する。
-     * @param lang639 言語コード
-     * @param content 詳細内容
-     * @return this本体
-     * @throws IOException 出力エラー
-     */
-    private PmdXmlExporter putDescription(CharSequence lang639,
-                                              CharSequence content)
-            throws IOException{
-        String text = content.toString();
-        text = text.replace(CRLF, LF);
-        text = text.replace(CR,   LF);
-
-        ind().putRawText("<description");
-        if( ! I18nText.CODE639_PRIMARY.equals(lang639) ){
-            sp().putAttr("lang", lang639).sp();
-        }
-        putRawText(">").ln();
-
-        putBRedContent(text);
-
-        ind().putRawText("</description>").ln();
-
-        if( ! hasOnlyBasicLatin(text) && isBasicLatinOnlyOut() ){
-            putBlockComment(text);
-        }
-
-        return this;
-    }
-
-    /**
-     * break要素を含む要素内容を出力する。
-     * 必要に応じてXML定義済み実体文字が割り振られた文字、
-     * コントロールコード、および非BasicLatin文字がエスケープされる。
-     * \nはbrタグに変換される。
-     * @param content 内容
-     * @return this本体
-     * @throws IOException 出力エラー
-     */
-    protected BasicXmlExporter putBRedContent(CharSequence content)
-            throws IOException{
-        int length = content.length();
-
-        int startPos = 0;
-
-        for(int idx = 0; idx < length; idx++){
-            char ch = content.charAt(idx);
-            if(ch == '\n'){
-                CharSequence seq = content.subSequence(startPos, idx);
-                putContent(seq).putRawText("<br/>").ln();
-                startPos = idx + 1;
-            }
-        }
-
-        if(startPos < length){
-            CharSequence seq = content.subSequence(startPos, length);
-            putContent(seq).ln();
-        }
-
-        return this;
-    }
-
-    /**
-     * 各種メタ情報を出力する。
-     * @param model モデルデータ
-     * @return this本体
-     * @throws IOException 出力エラー
-     */
-    private PmdXmlExporter putMetaInfo(PmdModel model) throws IOException{
-        ind().putRawText("<license>").ln();
-        ind().putRawText("</license>").ln(2);
-
-        ind().putRawText("<credits>").ln();
-        ind().putRawText("</credits>").ln(2);
-
-        if(this.generator != null){
-            ind().putRawText("<meta ");
-            putAttr("name", "generator").sp()
-                                        .putAttr("content", this.generator);
-            putRawText(" />").ln();
-        }
-
-        ind().putRawText("<meta ");
-        putAttr("name", "siteURL").sp().putAttr("content", "");
-        putRawText(" />").ln();
-        ind().putRawText("<meta ");
-        putAttr("name", "imageURL").sp().putAttr("content", "");
-        putRawText(" />").ln(2);
-
-        return this;
-    }
-
-    /**
-     * マテリアル素材一覧を出力する。
-     * @param model モデルデータ
-     * @return this本体
-     * @throws IOException 出力エラー
-     */
-    private PmdXmlExporter putMaterialList(PmdModel model)
-            throws IOException{
-        ind().putRawText("<materialList>").ln();
-
-        pushNest();
-        int ct = 0;
-        boolean dumped = false;
-        List<Material> materialList = model.getMaterialList();
-        for(Material material : materialList){
-            if( ! dumped ) ln();
-            putMaterial(material, ct++);
-            dumped = true;
-        }
-        popNest();
-
-        ind().putRawText("</materialList>").ln(2);
-
-        return this;
-    }
-
-    /**
-     * マテリアル素材情報を出力する。
-     * @param material マテリアル素材
-     * @param no マテリアル通し番号
-     * @return this本体
-     * @throws IOException 出力エラー
-     */
-    private PmdXmlExporter putMaterial(Material material, int no)
-            throws IOException{
-        String bool;
-        if(material.getEdgeAppearance()) bool = "true";
-        else                             bool = "false";
-        I18nText name = material.getMaterialName();
-        String primary = name.getPrimaryText();
-        String local = name.getText();
-
-        if(local != null && local.length() > 0){
-            ind().putLineComment(local).ln();
-        }
-        ind().putRawText("<material ");
-        if(primary != null && primary.length() > 0){
-            putAttr("name", primary).sp();
-        }
-
-        putAttr("showEdge", bool);
-        sp();
-        putNumberedIdAttr("surfaceGroupIdRef", PFX_SURFACEGROUP, no);
-        sp().putRawCh('>').ln();
-        pushNest();
-
-        putI18nName(name);
-
-        float[] rgba = new float[4];
-
-        Color diffuse = material.getDiffuseColor();
-        diffuse.getRGBComponents(rgba);
-        ind().putRawText("<diffuse ");
-        putFloatAttr("r", rgba[0]).sp();
-        putFloatAttr("g", rgba[1]).sp();
-        putFloatAttr("b", rgba[2]).sp();
-        putFloatAttr("alpha", rgba[3]).sp();
-        putRawText("/>").ln();
-
-        Color specular = material.getSpecularColor();
-        specular.getRGBComponents(rgba);
-        float shininess = material.getShininess();
-        ind().putRawText("<specular ");
-        putFloatAttr("r", rgba[0]).sp();
-        putFloatAttr("g", rgba[1]).sp();
-        putFloatAttr("b", rgba[2]).sp();
-        putFloatAttr("shininess", shininess).sp();
-        putRawText("/>").ln();
-
-        Color ambient = material.getAmbientColor();
-        ambient.getRGBComponents(rgba);
-        ind().putRawText("<ambient ");
-        putFloatAttr("r", rgba[0]).sp();
-        putFloatAttr("g", rgba[1]).sp();
-        putFloatAttr("b", rgba[2]).sp();
-        putRawText("/>").ln();
-
-        ShadeInfo shade = material.getShadeInfo();
-        String textureFileName = shade.getTextureFileName();
-        String spheremapFileName = shade.getSpheremapFileName();
-
-        if(shade.isValidToonIndex()){
-            ind().putRawText("<toon ");
-            int toonIdx = shade.getToonIndex();
-            putNumberedIdAttr("toonFileIdRef", PFX_TOONFILE, toonIdx);
-            putRawText(" />");
-            String toonFileName = shade.getToonFileName();
-            if(toonFileName != null && toonFileName.length() > 0){
-                sp().putLineComment(toonFileName);
-            }
-            ln();
-        }
-
-        if(textureFileName != null && textureFileName.length() > 0){
-            ind().putRawText("<textureFile ");
-            putAttr("winFileName", textureFileName);
-            putRawText(" />").ln();
-        }
-
-        if(spheremapFileName != null && spheremapFileName.length() > 0){
-            ind().putRawText("<spheremapFile ");
-            putAttr("winFileName", spheremapFileName);
-            putRawText(" />").ln();
-        }
-
-        popNest();
-        ind().putRawText("</material>").ln(2);
-
-        return this;
-    }
-
-    /**
-     * トゥーンファイルマッピング情報を出力する。
-     * @param model モデルデータ
-     * @return this本体
-     * @throws IOException 出力エラー
-     */
-    private PmdXmlExporter putToonMap(PmdModel model)
-            throws IOException{
-        ind().putRawText("<toonMap>").ln();
-        pushNest();
-
-        ToonMap map = model.getToonMap();
-        for(int index = 0; index <= 9; index++){
-            ind().putToon(map, index).ln();
-        }
-
-        popNest();
-        ind().putRawText("</toonMap>").ln(2);
-        return this;
-    }
-
-    /**
-     * 個別のトゥーンファイル情報を出力する。
-     * @param map トゥーンマップ
-     * @param index インデックス値
-     * @return this本体
-     * @throws IOException 出力エラー
-     */
-    private PmdXmlExporter putToon(ToonMap map, int index)
-            throws IOException{
-        putRawText("<toonDef ");
-        putNumberedIdAttr("toonFileId", PFX_TOONFILE, index).sp();
-        putIntAttr("index", index).sp();
-        String toonFile = map.getIndexedToon(index);
-        putAttr("winFileName", toonFile);
-        putRawText(" />");
-        putUnescapedComment(toonFile);
-        return this;
-    }
-
-    /**
-     * サーフェイスグループリストを出力する。
-     * @param model モデルデータ
-     * @return this本体
-     * @throws IOException 出力エラー
-     */
-    private PmdXmlExporter putSurfaceGroupList(PmdModel model)
-            throws IOException{
-        ind().putRawText("<surfaceGroupList>").ln();
-
-        pushNest();
-        int ct = 0;
-        boolean dumped = false;
-        List<Material> materialList = model.getMaterialList();
-        for(Material material : materialList){
-            List<Surface> surfaceList = material.getSurfaceList();
-            if( ! dumped ) ln();
-            putSurfaceList(surfaceList, ct++);
-            dumped = true;
-        }
-        popNest();
-
-        ind().putRawText("</surfaceGroupList>").ln(2);
-
-        return this;
-    }
-
-    /**
-     * 個別のサーフェイスグループを出力する。
-     * @param surfaceList サーフェイスのリスト
-     * @param index グループインデックス
-     * @return this本体
-     * @throws IOException 出力エラー
-     */
-    private PmdXmlExporter putSurfaceList(List<Surface> surfaceList,
-                                              int index)
-            throws IOException{
-        ind().putRawText("<surfaceGroup ");
-        putNumberedIdAttr("surfaceGroupId", PFX_SURFACEGROUP, index);
-        sp().putRawText(">").ln();
-        pushNest();
-
-        for(Surface surface : surfaceList){
-            putSurface(surface);
-        }
-
-        popNest();
-        ind().putRawText("</surfaceGroup>").ln(2);
-
-        return this;
-    }
-
-    /**
-     * 個別のサーフェイスを出力する。
-     * @param surface サーフェイス
-     * @return this本体
-     * @throws IOException 出力エラー
-     */
-    private PmdXmlExporter putSurface(Surface surface)
-            throws IOException{
-        ind().putRawText("<surface ");
-
-        Vertex vertex1 = surface.getVertex1();
-        Vertex vertex2 = surface.getVertex2();
-        Vertex vertex3 = surface.getVertex3();
-
-        putNumberedIdAttr("vtxIdRef1", PFX_VERTEX, vertex1).sp();
-        putNumberedIdAttr("vtxIdRef2", PFX_VERTEX, vertex2).sp();
-        putNumberedIdAttr("vtxIdRef3", PFX_VERTEX, vertex3).sp();
-
-        putRawText("/>").ln();
-        return this;
-    }
-
-    /**
-     * 頂点リストを出力する。
-     * @param model モデルデータ
-     * @return this本体
-     * @throws IOException 出力エラー
-     */
-    private PmdXmlExporter putVertexList(PmdModel model)
-            throws IOException{
-        ind().putRawText("<vertexList>").ln();
-
-        pushNest();
-        boolean dumped = false;
-        List<Vertex> vertexList = model.getVertexList();
-        for(Vertex vertex : vertexList){
-            if( ! dumped ) ln();
-            putVertex(vertex);
-            dumped = true;
-        }
-        popNest();
-
-        ind().putRawText("</vertexList>").ln(2);
-
-        return this;
-    }
-
-    /**
-     * 個別の頂点情報を出力する。
-     * @param vertex 頂点
-     * @return this本体
-     * @throws IOException 出力エラー
-     */
-    private PmdXmlExporter putVertex(Vertex vertex)
-            throws IOException{
-        String bool;
-        if(vertex.getEdgeAppearance()) bool = "true";
-        else                           bool = "false";
-
-        ind().putRawText("<vertex ");
-        putNumberedIdAttr("vtxId", PFX_VERTEX, vertex).sp();
-        putAttr("showEdge", bool);
-        sp().putRawText(">").ln();
-        pushNest();
-
-        MkPos3D position = vertex.getPosition();
-        ind().putPosition(position).ln();
-
-        MkVec3D normal = vertex.getNormal();
-        ind().putRawText("<normal ");
-        putFloatAttr("x", (float) normal.getXVal()).sp();
-        putFloatAttr("y", (float) normal.getYVal()).sp();
-        putFloatAttr("z", (float) normal.getZVal()).sp();
-        putRawText("/>").ln();
-
-        MkPos2D uvPos = vertex.getUVPosition();
-        ind().putRawText("<uvMap ");
-        putFloatAttr("u", (float) uvPos.getXpos()).sp();
-        putFloatAttr("v", (float) uvPos.getYpos()).sp();
-        putRawText("/>").ln();
-
-        BoneInfo boneA = vertex.getBoneA();
-        BoneInfo boneB = vertex.getBoneB();
-        int weight = vertex.getWeightA();
-        ind().putRawText("<skinning ");
-        putNumberedIdAttr("boneIdRef1", PFX_BONE, boneA).sp();
-        putNumberedIdAttr("boneIdRef2", PFX_BONE, boneB).sp();
-        putIntAttr("weightBalance", weight).sp();
-        putRawText("/>").ln();
-
-        popNest();
-        ind().putRawText("</vertex>").ln(2);
-
-        return this;
-    }
-
-    /**
-     * ボーンリストを出力する。
-     * @param model モデルデータ
-     * @return this本体
-     * @throws IOException 出力エラー
-     */
-    private PmdXmlExporter putBoneList(PmdModel model)
-            throws IOException{
-        ind().putRawText("<boneList>").ln();
-        pushNest();
-
-        boolean dumped = false;
-        for(BoneInfo bone : model.getBoneList()){
-            if( ! dumped ){
-                ln().putBlockComment(BONETYPE_COMMENT).ln();
-            }
-            putBone(bone);
-            dumped = true;
-        }
-
-        popNest();
-        ind().putRawText("</boneList>").ln(2);
-
-        return this;
-    }
-
-    /**
-     * 個別のボーン情報を出力する。
-     * @param bone ボーン情報
-     * @return this本体
-     * @throws IOException 出力エラー
-     */
-    private PmdXmlExporter putBone(BoneInfo bone)
-            throws IOException{
-        I18nText i18nName = bone.getBoneName();
-        BoneType type = bone.getBoneType();
-
-        StringBuilder boneComment = new StringBuilder();
-        String boneName = i18nName.getText();
-        if(boneName.isEmpty()){
-            boneName = "[NAMELESS]";
-        }
-        boneComment.append(boneName);
-        String typeName = type.getGuiName(DEF_LOCALE);
-        boneComment.append(" [").append(typeName).append(']');
-        ind().putLineComment(boneComment.toString()).ln();
-
-        ind().putRawText("<bone ");
-        putPrimaryNameAttr("name", i18nName).sp();
-        putNumberedIdAttr("boneId", PFX_BONE, bone).sp();
-        putAttr("type", type.name());
-        sp().putRawText(">").ln();
-        pushNest();
-
-        putI18nName(i18nName);
-
-        MkPos3D position = bone.getPosition();
-        ind().putPosition(position).ln();
-
-        BoneInfo srcBone = bone.getSrcBone();
-        if(bone.getBoneType() == BoneType.LINKEDROT){
-            ind().putRawText("<rotationRatio ");
-            putIntAttr("ratio", bone.getRotationRatio());
-            putRawText(" />").ln();
-        }else if(srcBone != null){
-            String iktag;
-            switch(getXmlFileType()){
-            case XML_101009:
-                iktag = "<ikBone ";
-                break;
-            case XML_130128:
-                iktag = "<sourceBone ";
-                break;
-            default:
-                assert false;
-                throw new AssertionError();
-            }
-            ind().putRawText(iktag);
-            putNumberedIdAttr("boneIdRef", PFX_BONE, srcBone);
-            putRawText(" /> ");
-            String ikBoneName = "Ref:" + srcBone.getBoneName().getText();
-            putLineComment(ikBoneName);
-            ln();
-        }
-
-        BoneInfo prev = bone.getPrevBone();
-        BoneInfo next = bone.getNextBone();
-
-        StringBuilder chainComment = new StringBuilder();
-        if(prev != null){
-            chainComment.append('[')
-                        .append(prev.getBoneName().getPrimaryText())
-                        .append(']')
-                        .append(" >>#");
-        }
-        if(next != null){
-            if(chainComment.length() <= 0) chainComment.append("#");
-            chainComment.append(">> ")
-                        .append('[')
-                        .append(next.getBoneName().getPrimaryText())
-                        .append(']');
-        }
-        if(chainComment.length() > 0){
-            ln();
-            ind().putLineComment(chainComment).ln();
-        }
-
-        ind().putRawText("<boneChain");
-        if(prev != null){
-            sp();
-            putNumberedIdAttr("prevBoneIdRef", PFX_BONE, prev);
-        }
-        if(next != null){
-            sp();
-            putNumberedIdAttr("nextBoneIdRef", PFX_BONE, next);
-        }
-        putRawText(" />").ln();
-
-        popNest();
-        ind().putRawText("</bone>").ln(2);
-
-        return this;
-    }
-
-    /**
-     * ボーングループリストを出力する。
-     * @param model モデルデータ
-     * @return this本体
-     * @throws IOException 出力エラー
-     */
-    private PmdXmlExporter putBoneGroupList(PmdModel model)
-            throws IOException{
-        ind().putRawText("<boneGroupList>").ln();
-
-        pushNest();
-        boolean dumped = false;
-        List<BoneGroup> groupList = model.getBoneGroupList();
-        for(BoneGroup group : groupList){
-            if(group.isDefaultBoneGroup()) continue;
-            if( ! dumped ) ln();
-            putBoneGroup(group);
-            dumped = true;
-        }
-        popNest();
-
-        ind().putRawText("</boneGroupList>").ln(2);
-
-        return this;
-    }
-
-    /**
-     * 個別のボーングループ情報を出力する。
-     * @param group ボーングループ情報
-     * @return this本体
-     * @throws IOException 出力エラー
-     */
-    private PmdXmlExporter putBoneGroup(BoneGroup group)
-            throws IOException{
-        I18nText i18nName = group.getGroupName();
-
-        putLocalNameComment(i18nName).ln();
-        ind().putRawText("<boneGroup ");
-        putPrimaryNameAttr("name", i18nName);
-        sp().putRawText(">").ln();
-        pushNest();
-
-        putI18nName(i18nName);
-
-        for(BoneInfo bone : group){
-            ind().putRawText("<boneGroupMember ");
-            putNumberedIdAttr("boneIdRef", PFX_BONE, bone);
-            putRawText(" /> ");
-            String boneName = "Ref:" + bone.getBoneName().getText();
-            putLineComment(boneName).ln();
-        }
-
-        popNest();
-        ind().putRawText("</boneGroup>").ln(2);
-
-        return this;
-    }
-
-    /**
-     * IKチェーンリストを出力する。
-     * @param model モデルデータ
-     * @return this本体
-     * @throws IOException 出力エラー
-     */
-    private PmdXmlExporter putIKChainList(PmdModel model)
-            throws IOException{
-        ind().putRawText("<ikChainList>").ln();
-
-        pushNest();
-        boolean dumped = false;
-        List<IKChain> chainList = model.getIKChainList();
-        for(IKChain chain : chainList){
-            if( ! dumped ) ln();
-            putIKChain(chain);
-            dumped = true;
-        }
-        popNest();
-
-        ind().putRawText("</ikChainList>").ln(2);
-
-        return this;
-    }
-
-    /**
-     * 個別のIKチェーン情報を出力する。
-     * @param chain チェーン情報
-     * @return this本体
-     * @throws IOException 出力エラー
-     */
-    private PmdXmlExporter putIKChain(IKChain chain)
-            throws IOException{
-        int depth = chain.getIKDepth();
-        float weight = chain.getIKWeight();
-        BoneInfo ikBone = chain.getIkBone();
-
-        ind().putLineComment("Ref:" + ikBone.getBoneName().getText()).ln();
-        ind().putRawText("<ikChain ");
-        putNumberedIdAttr("ikBoneIdRef", PFX_BONE, ikBone).sp();
-        putIntAttr("recursiveDepth", depth).sp();
-        putFloatAttr("weight", weight);
-        sp().putRawText(">").ln();
-        pushNest();
-
-        for(BoneInfo bone : chain){
-            ind().putRawText("<chainOrder ");
-            putNumberedIdAttr("boneIdRef", PFX_BONE, bone);
-            putRawText(" /> ");
-            putLineComment("Ref:" + bone.getBoneName().getText());
-            ln();
-        }
-
-        popNest();
-        ind().putRawText("</ikChain>").ln(2);
-
-        return this;
-    }
-
-    /**
-     * モーフリストを出力する。
-     * @param model モデルデータ
-     * @return this本体
-     * @throws IOException 出力エラー
-     */
-    private PmdXmlExporter putMorphList(PmdModel model)
-            throws IOException{
-        ind().putRawText("<morphList>").ln();
-        pushNest();
-
-        boolean dumped = false;
-        Map<MorphType, List<MorphPart>> morphMap = model.getMorphMap();
-        for(MorphType type : MorphType.values()){
-            if(type == MorphType.BASE) continue;
-            List<MorphPart> partList = morphMap.get(type);
-            if(partList == null) continue;
-            for(MorphPart part : partList){
-                if( ! dumped ){
-                    ln().putBlockComment(MORPHTYPE_COMMENT).ln();
-                }
-                putMorphPart(part);
-                dumped = true;
-            }
-        }
-
-        popNest();
-        ind().putRawText("</morphList>").ln(2);
-
-        return this;
-    }
-
-    /**
-     * 個別のモーフ情報を出力する。
-     * @param part モーフ情報
-     * @return this本体
-     * @throws IOException 出力エラー
-     */
-    private PmdXmlExporter putMorphPart(MorphPart part)
-            throws IOException{
-        I18nText i18nName = part.getMorphName();
-        String primary = i18nName.getPrimaryText();
-
-        putLocalNameComment(i18nName).ln();
-        ind().putRawText("<morph ");
-        putAttr("name", primary).sp();
-        putAttr("type", part.getMorphType().name());
-        sp().putRawText(">");
-        ln();
-        pushNest();
-
-        putI18nName(i18nName);
-
-        for(MorphVertex mvertex : part){
-            MkPos3D offset = mvertex.getOffset();
-            Vertex base = mvertex.getBaseVertex();
-
-            ind().putRawText("<morphVertex ");
-            putNumberedIdAttr("vtxIdRef", PFX_VERTEX, base).sp();
-            putFloatAttr("xOff", (float) offset.getXpos()).sp();
-            putFloatAttr("yOff", (float) offset.getYpos()).sp();
-            putFloatAttr("zOff", (float) offset.getZpos()).sp();
-            putRawText("/>");
-            ln();
-        }
-
-        popNest();
-        ind().putRawText("</morph>").ln(2);
-
-        return this;
-    }
-
-    /**
-     * 剛体リストを出力する。
-     * @param model モデルデータ
-     * @return this本体
-     * @throws IOException 出力エラー
-     */
-    private PmdXmlExporter putRigidList(PmdModel model)
-            throws IOException{
-        ind().putRawText("<rigidList>").ln();
-        pushNest();
-
-        boolean dumped = false;
-        for(RigidInfo rigid : model.getRigidList()){
-            if( ! dumped ){
-                ln().putBlockComment(RIGIDBEHAVIOR_COMMENT).ln();
-            }
-            putRigid(rigid);
-            dumped = true;
-        }
-
-        popNest();
-        ind().putRawText("</rigidList>").ln(2);
-
-        return this;
-    }
-
-    /**
-     * 個別の剛体情報を出力する。
-     * @param rigid 剛体情報
-     * @return this本体
-     * @throws IOException 出力エラー
-     */
-    private PmdXmlExporter putRigid(RigidInfo rigid)
-            throws IOException{
-        BoneInfo linkedBone = rigid.getLinkedBone();
-        I18nText i18nName = rigid.getRigidName();
-        String primary = i18nName.getPrimaryText();
-
-        putLocalNameComment(i18nName).ln();
-        ind().putRawText("<rigid ");
-        putAttr("name", primary).sp();
-        putNumberedIdAttr("rigidId", PFX_RIGID, rigid).sp();
-        putAttr("behavior", rigid.getBehaviorType().name());
-        sp().putRawText(">").ln();
-        pushNest();
-
-        putI18nName(i18nName);
-
-        if(linkedBone != null){
-            ind().putRawText("<linkedBone ");
-            putNumberedIdAttr("boneIdRef", PFX_BONE, linkedBone);
-            putRawText(" /> ");
-            putLineComment("Ref:" + linkedBone.getBoneName().getText());
-            ln(2);
-        }
-
-        RigidShape shape = rigid.getRigidShape();
-        putRigidShape(shape);
-
-        MkPos3D position = rigid.getPosition();
-        ind().putPosition(position).ln();
-
-        Rad3d rotation = rigid.getRotation();
-        ind().putRadRotation(rotation).ln();
-
-        DynamicsInfo dynamics = rigid.getDynamicsInfo();
-        putDynamics(dynamics).ln();
-
-        for(RigidGroup group : rigid.getThroughGroupColl()){
-            ind().putRawText("<throughRigidGroup ");
-            putNumberedIdAttr("rigidGroupIdRef",
-                              PFX_RIGIDGROUP,
-                              group.getSerialNumber() + 1).sp();
-            putRawText(" />").ln();
-        }
-
-        popNest();
-        ind().putRawText("</rigid>").ln(2);
-
-        return this;
-    }
-
-    /**
-     * 剛体形状を出力する。
-     * @param shape 剛体形状
-     * @return this本体
-     * @throws IOException 出力エラー
-     */
-    private PmdXmlExporter putRigidShape(RigidShape shape)
-            throws IOException{
-        RigidShapeType type = shape.getShapeType();
-
-        switch(type){
-        case BOX:
-            ind().putRawText("<rigidShapeBox ");
-            putFloatAttr("width", shape.getWidth()).sp();
-            putFloatAttr("height", shape.getHeight()).sp();
-            putFloatAttr("depth", shape.getDepth()).sp();
-            break;
-        case SPHERE:
-            ind().putRawText("<rigidShapeSphere ");
-            putFloatAttr("radius", shape.getRadius()).sp();
-            break;
-        case CAPSULE:
-            ind().putRawText("<rigidShapeCapsule ");
-            putFloatAttr("height", shape.getHeight()).sp();
-            putFloatAttr("radius", shape.getRadius()).sp();
-            break;
-        default:
-            assert false;
-            throw new AssertionError();
-        }
-
-        putRawText("/>").ln();
-
-        return this;
-    }
-
-    /**
-     * 力学設定を出力する。
-     * @param dynamics 力学設定
-     * @return this本体
-     * @throws IOException 出力エラー
-     */
-    private PmdXmlExporter putDynamics(DynamicsInfo dynamics)
-            throws IOException{
-        ind().putRawText("<dynamics").ln();
-        pushNest();
-        ind().putFloatAttr("mass", dynamics.getMass()).ln();
-        ind().putFloatAttr("dampingPosition",
-                dynamics.getDampingPosition()).ln();
-        ind().putFloatAttr("dampingRotation",
-                dynamics.getDampingRotation()).ln();
-        ind().putFloatAttr("restitution", dynamics.getRestitution()).ln();
-        ind().putFloatAttr("friction", dynamics.getFriction()).ln();
-        popNest();
-        ind().putRawText("/>").ln();
-
-        return this;
-    }
-
-    /**
-     * 剛体グループリストを出力する。
-     * @param model モデルデータ
-     * @return this本体
-     * @throws IOException 出力エラー
-     */
-    private PmdXmlExporter putRigidGroupList(PmdModel model)
-            throws IOException{
-        ind().putRawText("<rigidGroupList>").ln(2);
-        pushNest();
-
-        boolean singleLast = false;
-        for(RigidGroup group : model.getRigidGroupList()){
-            List<RigidInfo> rigidList = group.getRigidList();
-            if(singleLast &&  ! rigidList.isEmpty()){
-                ln();
-            }
-            ind().putRawText("<rigidGroup ");
-            putNumberedIdAttr("rigidGroupId",
-                              PFX_RIGIDGROUP,
-                              group.getSerialNumber() + 1);
-            if(rigidList.isEmpty()){
-                putRawText(" />").ln();
-                singleLast = true;
-                continue;
-            }
-            putRawText(" >").ln();
-            pushNest();
-
-            for(RigidInfo rigid : rigidList){
-                ind().putRawText("<rigidGroupMember ");
-                putNumberedIdAttr("rigidIdRef", PFX_RIGID, rigid).sp();
-                putRawText("/>");
-                sp();
-                putLineComment("Ref:" + rigid.getRigidName().getText());
-                ln();
-            }
-
-            popNest();
-            ind().putRawText("</rigidGroup>").ln(2);
-            singleLast = false;
-        }
-
-        if(singleLast){
-            ln();
-        }
-
-        popNest();
-        ind().putRawText("</rigidGroupList>").ln(2);
-
-        return this;
-    }
-
-    /**
-     * ジョイントリストを出力する。
-     * @param model モデルデータ
-     * @return this本体
-     * @throws IOException 出力エラー
-     */
-    private PmdXmlExporter putJointList(PmdModel model)
-            throws IOException{
-        ind().putRawText("<jointList>").ln();
-
-        pushNest();
-        boolean dumped = false;
-        List<JointInfo> jointList = model.getJointList();
-        for(JointInfo joint : jointList){
-            if( ! dumped ) ln();
-            putJoint(joint);
-            dumped = true;
-        }
-        popNest();
-
-        ind().putRawText("</jointList>").ln(2);
-
-        return this;
-    }
-
-    /**
-     * 個別のジョイント情報を出力する。
-     * @param joint ジョイント情報
-     * @return this本体
-     * @throws IOException 出力エラー
-     */
-    private PmdXmlExporter putJoint(JointInfo joint)
-            throws IOException{
-        I18nText i18nName = joint.getJointName();
-
-        putLocalNameComment(i18nName).ln();
-        ind().putRawText("<joint ");
-        putPrimaryNameAttr("name", i18nName);
-        sp().putRawText(">").ln();
-        pushNest();
-
-        putI18nName(i18nName);
-
-        RigidInfo rigidA = joint.getRigidA();
-        RigidInfo rigidB = joint.getRigidB();
-
-        ind();
-        putLineComment("[" + rigidA.getRigidName().getText() + "]"
-                + " <=> [" + rigidB.getRigidName().getText() + "]");
-        ln();
-
-        ind().putRawText("<jointedRigidPair ");
-        putNumberedIdAttr("rigidIdRef1", PFX_RIGID, rigidA).sp();
-        putNumberedIdAttr("rigidIdRef2", PFX_RIGID, rigidB).sp();
-        putRawText("/>").ln(2);
-
-        MkPos3D position = joint.getPosition();
-        ind().putPosition(position).ln();
-
-        TripletRange posRange = joint.getPositionRange();
-        ind().putRawText("<limitPosition").ln();
-        pushNest();
-        ind();
-        putFloatAttr("xFrom", posRange.getXFrom()).sp();
-        putFloatAttr("xTo",   posRange.getXTo()).ln();
-        ind();
-        putFloatAttr("yFrom", posRange.getYFrom()).sp();
-        putFloatAttr("yTo",   posRange.getYTo()).ln();
-        ind();
-        putFloatAttr("zFrom", posRange.getZFrom()).sp();
-        putFloatAttr("zTo",   posRange.getZTo()).ln();
-        popNest();
-        ind().putRawText("/>").ln(2);
-
-        Rad3d rotation = joint.getRotation();
-        ind().putRadRotation(rotation).ln();
-        TripletRange rotRange = joint.getRotationRange();
-        ind().putRawText("<limitRotation").ln();
-        pushNest();
-        ind();
-        putFloatAttr("xFrom", rotRange.getXFrom()).sp();
-        putFloatAttr("xTo",   rotRange.getXTo()).ln();
-        ind();
-        putFloatAttr("yFrom", rotRange.getYFrom()).sp();
-        putFloatAttr("yTo",   rotRange.getYTo()).ln();
-        ind();
-        putFloatAttr("zFrom", rotRange.getZFrom()).sp();
-        putFloatAttr("zTo",   rotRange.getZTo()).ln();
-        popNest();
-        ind().putRawText("/>").ln(2);
-
-        MkPos3D elaPosition = joint.getElasticPosition();
-        ind().putRawText("<elasticPosition ");
-        putFloatAttr("x", (float) elaPosition.getXpos()).sp();
-        putFloatAttr("y", (float) elaPosition.getYpos()).sp();
-        putFloatAttr("z", (float) elaPosition.getZpos()).sp();
-        putRawText("/>").ln();
-
-        Deg3d elaRotation = joint.getElasticRotation();
-        ind().putRawText("<elasticRotation ");
-        putFloatAttr("xDeg", elaRotation.getXDeg()).sp();
-        putFloatAttr("yDeg", elaRotation.getYDeg()).sp();
-        putFloatAttr("zDeg", elaRotation.getZDeg()).sp();
-        putRawText("/>").ln(2);
-
-        popNest();
-        ind().putRawText("</joint>").ln(2);
-
-        return this;
-    }
-
-}
diff --git a/src/main/java/jp/sfjp/mikutoga/pmd/xml/XmlLoader.java b/src/main/java/jp/sfjp/mikutoga/pmd/xml/XmlLoader.java
deleted file mode 100644 (file)
index d0160db..0000000
+++ /dev/null
@@ -1,1113 +0,0 @@
-/*
- * xml loader
- *
- * License : The MIT License
- * Copyright(c) 2010 MikuToga Partners
- */
-
-package jp.sfjp.mikutoga.pmd.xml;
-
-import java.awt.Color;
-import java.io.IOException;
-import java.text.MessageFormat;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import javax.xml.parsers.DocumentBuilder;
-import jp.sfjp.mikutoga.corelib.I18nText;
-import jp.sfjp.mikutoga.math.MkPos2D;
-import jp.sfjp.mikutoga.math.MkPos3D;
-import jp.sfjp.mikutoga.math.MkVec3D;
-import jp.sfjp.mikutoga.pmd.BoneType;
-import jp.sfjp.mikutoga.pmd.Deg3d;
-import jp.sfjp.mikutoga.pmd.MorphType;
-import jp.sfjp.mikutoga.pmd.Rad3d;
-import jp.sfjp.mikutoga.pmd.RigidBehaviorType;
-import jp.sfjp.mikutoga.pmd.RigidShapeType;
-import jp.sfjp.mikutoga.pmd.TripletRange;
-import jp.sfjp.mikutoga.pmd.model.BoneGroup;
-import jp.sfjp.mikutoga.pmd.model.BoneInfo;
-import jp.sfjp.mikutoga.pmd.model.DynamicsInfo;
-import jp.sfjp.mikutoga.pmd.model.IKChain;
-import jp.sfjp.mikutoga.pmd.model.JointInfo;
-import jp.sfjp.mikutoga.pmd.model.ListUtil;
-import jp.sfjp.mikutoga.pmd.model.Material;
-import jp.sfjp.mikutoga.pmd.model.MorphPart;
-import jp.sfjp.mikutoga.pmd.model.MorphVertex;
-import jp.sfjp.mikutoga.pmd.model.PmdModel;
-import jp.sfjp.mikutoga.pmd.model.RigidGroup;
-import jp.sfjp.mikutoga.pmd.model.RigidInfo;
-import jp.sfjp.mikutoga.pmd.model.RigidShape;
-import jp.sfjp.mikutoga.pmd.model.ShadeInfo;
-import jp.sfjp.mikutoga.pmd.model.Surface;
-import jp.sfjp.mikutoga.pmd.model.ToonMap;
-import jp.sfjp.mikutoga.pmd.model.Vertex;
-import jp.sourceforge.mikutoga.xml.DomNsUtils;
-import jp.sourceforge.mikutoga.xml.DomUtils;
-import jp.sourceforge.mikutoga.xml.TogaXmlException;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-
-/**
- * XML形式でのモデルファイルを読み込む。
- */
-public class XmlLoader {
-
-    private static final String ERR_INVROOT =
-            "invalid root element[{0}]";
-    private static final String ERR_UKVER =
-            "unknown schema version[{0}]";
-
-
-    private PmdModel model;
-
-    private final Map<String, Integer> toonIdxMap =
-            new HashMap<String, Integer>();
-    private final Map<String, BoneInfo> boneMap =
-            new HashMap<String, BoneInfo>();
-    private final Map<String, Vertex> vertexMap =
-            new HashMap<String, Vertex>();
-    private final Map<String, List<Surface>> surfaceGroupMap =
-            new HashMap<String, List<Surface>>();
-    private final Map<String, RigidInfo> rigidMap =
-            new HashMap<String, RigidInfo>();
-    private final Map<String, RigidGroup> rigidGroupMap =
-            new HashMap<String, RigidGroup>();
-
-    private String rootNamespace = Schema130128.NS_PMDXML;
-    private XmlModelFileType fileType = XmlModelFileType.XML_AUTO;
-
-
-    /**
-     * コンストラクタ。
-     */
-    public XmlLoader(){
-        super();
-        return;
-    }
-
-
-    /**
-     * 要素からxsd:string型属性値を読み取る。
-     * @param elem 要素
-     * @param attrName 属性名
-     * @return 文字列
-     * @throws TogaXmlException 属性値が見つからなかった。
-     */
-    private static String getStringAttr(Element elem, String attrName)
-            throws TogaXmlException{
-        return DomUtils.getStringAttr(elem, attrName);
-    }
-
-    /**
-     * 要素からxsd:boolean型属性値を読み取る。
-     * @param elem 要素
-     * @param attrName 属性名
-     * @return 真ならtrue
-     * @throws TogaXmlException 属性値が見つからなかった。
-     */
-    private static boolean getBooleanAttr(Element elem, String attrName)
-            throws TogaXmlException{
-        return DomUtils.getBooleanAttr(elem, attrName);
-    }
-
-    /**
-     * 要素からxsd:integer型属性値を読み取る。
-     * @param elem 要素
-     * @param attrName 属性名
-     * @return int値
-     * @throws TogaXmlException 属性値が見つからなかった。
-     */
-    private static int getIntegerAttr(Element elem, String attrName)
-            throws TogaXmlException{
-        return DomUtils.getIntegerAttr(elem, attrName);
-    }
-
-    /**
-     * 要素からxsd:float型属性値を読み取る。
-     * @param elem 要素
-     * @param attrName 属性名
-     * @return float値
-     * @throws TogaXmlException 属性値が見つからなかった。
-     */
-    private static float getFloatAttr(Element elem, String attrName)
-            throws TogaXmlException{
-        return DomUtils.getFloatAttr(elem, attrName);
-    }
-
-    /**
-     * 要素から日本語Windows用ファイル名を属性値として読み取る。
-     * 念のため文字U+00A5は文字U-005Cに変換される。
-     * @param elem 要素
-     * @param attrName 属性名
-     * @return ファイル名
-     * @throws TogaXmlException 属性値が見つからなかった。
-     */
-    private static String getSjisFileNameAttr(Element elem, String attrName)
-            throws TogaXmlException{
-        return DomUtils.getSjisFileNameAttr(elem, attrName);
-    }
-
-    /**
-     * brタグで区切られた文字列内容(Mixed content)を
-     * 改行付き文字列に変換する。
-     * brタグはその出現回数だけ\nに変換される。
-     * 生文字列コンテンツ中の\n,\rは削除される。
-     * 改行文字以外のホワイトスペースは保持される。
-     * @param parent br要素及び文字列コンテンツを含む要素
-     * @return 変換された文字列
-     */
-    private static String getBRedContent(Element parent){
-        StringBuilder result = new StringBuilder();
-
-        for(Node node = parent.getFirstChild();
-            node != null;
-            node = node.getNextSibling() ){
-
-            switch(node.getNodeType()){
-            case Node.ELEMENT_NODE:
-                Element elem = (Element) node;
-                if("br".equals(elem.getTagName())){
-                    result.append('\n');
-                }
-                break;
-            case Node.TEXT_NODE:
-            case Node.CDATA_SECTION_NODE:
-                String content = node.getTextContent();
-                content = content.replace("\r", "");
-                content = content.replace("\n", "");
-                result.append(content);
-                break;
-            default:
-                break;
-            }
-        }
-
-        return result.toString();
-    }
-
-
-    /**
-     * パース中のXMLファイル種別を返す。
-     * @return ファイル種別
-     */
-    private XmlModelFileType getFileType(){
-        return this.fileType;
-    }
-
-    /**
-     * パース中のXMLファイル種別を設定する。
-     * @param type 具体的なファイル種別
-     */
-    private void setFileType(XmlModelFileType type){
-        if(   type != XmlModelFileType.XML_101009
-           && type != XmlModelFileType.XML_130128 ){
-            throw new IllegalArgumentException();
-        }
-        this.fileType = type;
-        return;
-    }
-
-    /**
-     * ルート要素の名前空間URIを返す。
-     * @return 名前空間URI。nullなら名前空間が無いと見なされる
-     */
-    private String getRootNamespace(){
-        return this.rootNamespace;
-    }
-
-    /**
-     * ルート要素の名前空間URIを設定する。
-     * @param name 名前空間URI。nullなら名前空間が無いと見なされる
-     */
-    private void setRootNamespace(String name){
-        this.rootNamespace = name;
-        return;
-    }
-
-    /**
-     * 指定された名前の子要素を1つだけ返す。
-     * @param parent 親要素
-     * @param tagName 子要素名
-     * @return 子要素
-     * @throws TogaXmlException 1つも見つからなかった
-     */
-    private Element getChild(Element parent, String tagName)
-            throws TogaXmlException{
-        String ns = getRootNamespace();
-        Element result = DomNsUtils.getFirstChild(parent, ns, tagName);
-        return result;
-    }
-
-    /**
-     * 親要素が指定された名前の子要素を持つか判定する。
-     * @param parent 親要素
-     * @param tagName 子要素名
-     * @return 指定名の子要素が存在すればtrue
-     */
-    private boolean hasChild(Element parent, String tagName){
-        String ns = getRootNamespace();
-        return DomNsUtils.hasChild(parent, ns, tagName);
-    }
-
-    /**
-     * 指定された名前の子要素のforeachを返す。
-     * @param parent 親要素
-     * @param childTag 子要素名
-     * @return 子要素のforeach
-     */
-    private Iterable<Element> eachChild(Element parent,
-                                         String childTag){
-        String ns = getRootNamespace();
-        return DomNsUtils.getEachChild(parent, ns, childTag);
-    }
-
-    /**
-     * 多言語名を取得する。
-     * @param baseElement 元要素
-     * @param text 多言語名格納先
-     * @throws TogaXmlException あるべき属性が存在しない。
-     */
-    private void buildI18nName(Element baseElement, I18nText text)
-            throws TogaXmlException{
-        String primaryText;
-        primaryText = getStringAttr(baseElement, "name");
-        text.setPrimaryText(primaryText);
-
-        for(Element i18nNameElem : eachChild(baseElement, "i18nName")){
-            String lang = getStringAttr(i18nNameElem, "lang");
-            String name = getStringAttr(i18nNameElem, "name");
-            if("en".equals(lang)){
-                text.setGlobalText(name);
-            }else{
-                text.setI18nText(lang, text);
-            }
-        }
-
-        return;
-    }
-
-    /**
-     * XMLのパースを開始する。
-     * @param builder ドキュメントビルダ
-     * @param source XML入力
-     * @return モデルデータ
-     * @throws SAXException 構文エラー
-     * @throws IOException 入力エラー
-     * @throws TogaXmlException 構文エラー
-     */
-    public PmdModel parse(DocumentBuilder builder, InputSource source)
-            throws SAXException, IOException, TogaXmlException{
-        Document document = builder.parse(source);
-        PmdModel result = parse(document);
-        return result;
-    }
-
-    /**
-     * XMLのパースを開始する。
-     * @param document DOMドキュメント
-     * @return モデルデータ
-     * @throws TogaXmlException 構文エラー
-     */
-    public PmdModel parse(Document document)
-            throws TogaXmlException{
-        this.model = new PmdModel();
-
-        Element pmdModelElem = document.getDocumentElement();
-        String namespace = pmdModelElem.getNamespaceURI();
-        setRootNamespace(namespace);
-
-        buildBasicInfo(pmdModelElem);
-
-        buildBoneList(pmdModelElem);
-        buildVertexList(pmdModelElem);
-        buildSurfaceList(pmdModelElem);
-
-        buildToonMap(pmdModelElem);
-        buildMaterialList(pmdModelElem);
-        buildIkChainList(pmdModelElem);
-        buildMorphList(pmdModelElem);
-        buildBoneGroupList(pmdModelElem);
-
-        buildRigidList(pmdModelElem);
-        buildRigidGroupList(pmdModelElem);
-        resolveThroughRigidGroup(pmdModelElem);
-
-        buildJointList(pmdModelElem);
-
-        return this.model;
-    }
-
-    /**
-     * DOMからモデル基本情報を組み立てる。
-     * @param pmdModelElem ルート要素
-     * @throws TogaXmlException 構文エラー
-     */
-    private void buildBasicInfo(Element pmdModelElem)
-            throws TogaXmlException{
-        if( ! DomNsUtils.hasNsLocalNameElem(pmdModelElem,
-                                            getRootNamespace(),
-                                            "pmdModel") ){
-            String tagName = pmdModelElem.getTagName();
-            String msg = MessageFormat.format(ERR_INVROOT, tagName);
-            throw new TogaXmlException(msg);
-        }
-
-        String version = getStringAttr(pmdModelElem, "schemaVersion");
-        if(Schema101009.VER_PMDXML.equals(version)){
-            setFileType(XmlModelFileType.XML_101009);
-        }else if(Schema130128.VER_PMDXML.equals(version)){
-            setFileType(XmlModelFileType.XML_130128);
-        }else{
-            String msg = MessageFormat.format(ERR_UKVER, version);
-            throw new TogaXmlException(msg);
-        }
-
-        I18nText modelName = this.model.getModelName();
-        buildI18nName(pmdModelElem, modelName);
-
-        String primaryDescription = null;
-        String globalDescription = null;
-        for(Element descriptionElem :
-            eachChild(pmdModelElem, "description")){
-            String descriptionText = getBRedContent(descriptionElem);
-            if( ! descriptionElem.hasAttribute("lang") ){
-                primaryDescription = descriptionText;
-            }else{
-                String lang = getStringAttr(descriptionElem, "lang");
-                if(lang.equals("ja")){
-                    primaryDescription = descriptionText;
-                }else if(lang.equals("en")){
-                    globalDescription = descriptionText;
-                }
-            }
-        }
-
-        I18nText description = this.model.getDescription();
-        description.setPrimaryText(primaryDescription);
-        description.setGlobalText(globalDescription);
-
-        return;
-    }
-
-    /**
-     * DOMからボーンリスト情報を組み立てる。
-     * @param pmdModelElem ルート要素
-     * @throws TogaXmlException 構文エラー
-     */
-    private void buildBoneList(Element pmdModelElem)
-            throws TogaXmlException{
-        Element boneListElem = getChild(pmdModelElem, "boneList");
-
-        List<BoneInfo> boneList = this.model.getBoneList();
-
-        for(Element boneElem : eachChild(boneListElem, "bone")){
-            BoneInfo boneInfo = new BoneInfo();
-            boneList.add(boneInfo);
-
-            I18nText boneName = boneInfo.getBoneName();
-            buildI18nName(boneElem, boneName);
-
-            String boneType = getStringAttr(boneElem, "type");
-            BoneType type = BoneType.valueOf(boneType);
-            boneInfo.setBoneType(type);
-
-            String boneId = getStringAttr(boneElem, "boneId");
-            this.boneMap.put(boneId, boneInfo);
-
-            Element positionElem = getChild(boneElem, "position");
-            float xPos = getFloatAttr(positionElem, "x");
-            float yPos = getFloatAttr(positionElem, "y");
-            float zPos = getFloatAttr(positionElem, "z");
-            MkPos3D position = boneInfo.getPosition();
-            position.setXpos(xPos);
-            position.setYpos(yPos);
-            position.setZpos(zPos);
-        }
-
-        ListUtil.assignIndexedSerial(boneList);
-
-        Iterator<BoneInfo> bit = boneList.iterator();
-        for(Element boneElem : eachChild(boneListElem, "bone")){
-            BoneInfo boneInfo = bit.next();
-
-            if(hasChild(boneElem, "ikBone")){            // 101009 only
-                Element ikBoneElem = getChild(boneElem, "ikBone");
-                String ikBoneId = getStringAttr(ikBoneElem, "boneIdRef");
-                BoneInfo ikBone = this.boneMap.get(ikBoneId);
-                boneInfo.setSrcBone(ikBone);
-            }else if(hasChild(boneElem, "sourceBone")){  // 130128 only
-                Element srcBoneElem = getChild(boneElem, "sourceBone");
-                String srcBoneId = getStringAttr(srcBoneElem, "boneIdRef");
-                BoneInfo srcBone = this.boneMap.get(srcBoneId);
-                boneInfo.setSrcBone(srcBone);
-            }else if(hasChild(boneElem, "rotationRatio")){
-                Element rotElem = getChild(boneElem, "rotationRatio");
-                int ratio = getIntegerAttr(rotElem, "ratio");
-                boneInfo.setRotationRatio(ratio);
-            }
-
-            Element boneChainElem = getChild(boneElem, "boneChain");
-            if(boneChainElem.hasAttribute("prevBoneIdRef")){
-                String prevId = getStringAttr(boneChainElem, "prevBoneIdRef");
-                BoneInfo prevBone = this.boneMap.get(prevId);
-                boneInfo.setPrevBone(prevBone);
-            }
-            if(boneChainElem.hasAttribute("nextBoneIdRef")){
-                String nextId = getStringAttr(boneChainElem, "nextBoneIdRef");
-                BoneInfo nextBone = this.boneMap.get(nextId);
-                boneInfo.setNextBone(nextBone);
-            }
-        }
-
-        return;
-    }
-
-    /**
-     * DOMから頂点リスト情報を組み立てる。
-     * @param pmdModelElem ルート要素
-     * @throws TogaXmlException 構文エラー
-     */
-    private void buildVertexList(Element pmdModelElem)
-            throws TogaXmlException{
-        Element vertexListElem = getChild(pmdModelElem, "vertexList");
-
-        List<Vertex> vertexList = this.model.getVertexList();
-
-        for(Element vertexElem : eachChild(vertexListElem, "vertex")){
-            Vertex vertex = new Vertex();
-            vertexList.add(vertex);
-
-            String vertexId = getStringAttr(vertexElem, "vtxId");
-            this.vertexMap.put(vertexId, vertex);
-
-            boolean showEdge = getBooleanAttr(vertexElem, "showEdge");
-            vertex.setEdgeAppearance(showEdge);
-
-            float xVal;
-            float yVal;
-            float zVal;
-
-            Element positionElem = getChild(vertexElem, "position");
-            xVal = getFloatAttr(positionElem, "x");
-            yVal = getFloatAttr(positionElem, "y");
-            zVal = getFloatAttr(positionElem, "z");
-            MkPos3D position = vertex.getPosition();
-            position.setXpos(xVal);
-            position.setYpos(yVal);
-            position.setZpos(zVal);
-
-            Element normalElem = getChild(vertexElem, "normal");
-            xVal = getFloatAttr(normalElem, "x");
-            yVal = getFloatAttr(normalElem, "y");
-            zVal = getFloatAttr(normalElem, "z");
-            MkVec3D normal = vertex.getNormal();
-            normal.setXVal(xVal);
-            normal.setYVal(yVal);
-            normal.setZVal(zVal);
-
-            Element uvElem = getChild(vertexElem, "uvMap");
-            float uVal = getFloatAttr(uvElem, "u");
-            float vVal = getFloatAttr(uvElem, "v");
-            MkPos2D uv = vertex.getUVPosition();
-            uv.setXpos(uVal);
-            uv.setYpos(vVal);
-
-            Element skinningElem = getChild(vertexElem, "skinning");
-            String boneId1 = getStringAttr(skinningElem, "boneIdRef1");
-            String boneId2 = getStringAttr(skinningElem, "boneIdRef2");
-            int weight = getIntegerAttr(skinningElem, "weightBalance");
-            BoneInfo boneA = this.boneMap.get(boneId1);
-            BoneInfo boneB = this.boneMap.get(boneId2);
-            vertex.setBonePair(boneA, boneB);
-            vertex.setWeightA(weight);
-        }
-
-        ListUtil.assignIndexedSerial(vertexList);
-
-        return;
-    }
-
-    /**
-     * DOMからポリゴンリスト情報を組み立てる。
-     * @param pmdModelElem ルート要素
-     * @throws TogaXmlException 構文エラー
-     */
-    private void buildSurfaceList(Element pmdModelElem)
-            throws TogaXmlException{
-        Element surfaceGroupListElem =
-                getChild(pmdModelElem, "surfaceGroupList");
-
-        for(Element surfaceGroupElem :
-            eachChild(surfaceGroupListElem, "surfaceGroup") ){
-
-            String groupId =
-                    getStringAttr(surfaceGroupElem, "surfaceGroupId");
-            List<Surface> surfaceList = buildSurface(surfaceGroupElem);
-
-            this.surfaceGroupMap.put(groupId, surfaceList);
-        }
-
-        return;
-    }
-
-    /**
-     * DOMからポリゴン情報を組み立てる。
-     * @param surfaceGroupElem surfaceGroup要素
-     * @return ポリゴンリスト
-     * @throws TogaXmlException 構文エラー
-     */
-    private List<Surface> buildSurface(Element surfaceGroupElem)
-            throws TogaXmlException{
-        List<Surface> result = new ArrayList<Surface>();
-
-        for(Element surfaceElem : eachChild(surfaceGroupElem, "surface")){
-            Surface surface = new Surface();
-            result.add(surface);
-
-            String id1 = getStringAttr(surfaceElem, "vtxIdRef1");
-            String id2 = getStringAttr(surfaceElem, "vtxIdRef2");
-            String id3 = getStringAttr(surfaceElem, "vtxIdRef3");
-
-            Vertex vertex1 = this.vertexMap.get(id1);
-            Vertex vertex2 = this.vertexMap.get(id2);
-            Vertex vertex3 = this.vertexMap.get(id3);
-
-            surface.setTriangle(vertex1, vertex2, vertex3);
-        }
-
-        return result;
-    }
-
-    /**
-     * DOMからトゥーンマップ情報を組み立てる。
-     * @param pmdModelElem ルート要素
-     * @throws TogaXmlException 構文エラー
-     */
-    private void buildToonMap(Element pmdModelElem)
-            throws TogaXmlException{
-        ToonMap toonMap = this.model.getToonMap();
-
-        Element toonMapElem = getChild(pmdModelElem, "toonMap");
-
-        for(Element toonDefElem : eachChild(toonMapElem, "toonDef")){
-            String toonFileId = getStringAttr(toonDefElem, "toonFileId");
-            int toonIndex = getIntegerAttr(toonDefElem, "index");
-            String toonFile = getSjisFileNameAttr(toonDefElem, "winFileName");
-
-            toonMap.setIndexedToon(toonIndex, toonFile);
-            this.toonIdxMap.put(toonFileId, toonIndex);
-        }
-
-        return;
-    }
-
-    /**
-     * DOMからマテリアル情報を組み立てる。
-     * @param pmdModelElem ルート要素
-     * @throws TogaXmlException 構文エラー
-     */
-    private void buildMaterialList(Element pmdModelElem)
-            throws TogaXmlException{
-        Element materialListElem =
-                getChild(pmdModelElem, "materialList");
-
-        List<Surface> surfaceList = this.model.getSurfaceList();
-        List<Material> materialList = this.model.getMaterialList();
-
-        for(Element materialElem : eachChild(materialListElem, "material")){
-            Material material = new Material();
-            materialList.add(material);
-
-            material.getShadeInfo().setToonMap(this.model.getToonMap());
-
-            String surfaceGroupId =
-                    getStringAttr(materialElem, "surfaceGroupIdRef");
-            List<Surface> surfaceGroup =
-                    this.surfaceGroupMap.get(surfaceGroupId);
-            surfaceList.addAll(surfaceGroup);
-            material.getSurfaceList().addAll(surfaceGroup);
-
-            boolean hasEdge = getBooleanAttr(materialElem, "showEdge");
-            material.setEdgeAppearance(hasEdge);
-
-            ShadeInfo shadeInfo = material.getShadeInfo();
-
-            int toonIdx;
-            if(hasChild(materialElem, "toon")){
-                Element toonElem = getChild(materialElem, "toon");
-                String toonId = getStringAttr(toonElem, "toonFileIdRef");
-                toonIdx = this.toonIdxMap.get(toonId);
-            }else{
-                toonIdx = 255;
-            }
-            shadeInfo.setToonIndex(toonIdx);
-
-            if(hasChild(materialElem, "textureFile")){
-                Element textureFileElem =
-                        getChild(materialElem, "textureFile");
-                String textureFile =
-                        getSjisFileNameAttr(textureFileElem, "winFileName");
-                shadeInfo.setTextureFileName(textureFile);
-            }
-
-            if(hasChild(materialElem, "spheremapFile")){
-                Element spheremapFileElem =
-                        getChild(materialElem, "spheremapFile");
-                String spheremapFile =
-                        getSjisFileNameAttr(spheremapFileElem, "winFileName");
-                shadeInfo.setSpheremapFileName(spheremapFile);
-            }
-
-            float red;
-            float green;
-            float blue;
-
-            Element diffuseElem = getChild(materialElem, "diffuse");
-            red   = getFloatAttr(diffuseElem, "r");
-            green = getFloatAttr(diffuseElem, "g");
-            blue  = getFloatAttr(diffuseElem, "b");
-            float alpha = getFloatAttr(diffuseElem, "alpha");
-            Color diffuse = new Color(red, green, blue, alpha);
-            material.setDiffuseColor(diffuse);
-
-            Element specularElem = getChild(materialElem, "specular");
-            red   = getFloatAttr(specularElem, "r");
-            green = getFloatAttr(specularElem, "g");
-            blue  = getFloatAttr(specularElem, "b");
-            float shininess = getFloatAttr(specularElem, "shininess");
-            Color specular = new Color(red, green, blue);
-            material.setSpecularColor(specular);
-            material.setShininess(shininess);
-
-            Element ambientElem = getChild(materialElem, "ambient");
-            red   = getFloatAttr(ambientElem, "r");
-            green = getFloatAttr(ambientElem, "g");
-            blue  = getFloatAttr(ambientElem, "b");
-            Color ambient = new Color(red, green, blue);
-            material.setAmbientColor(ambient);
-        }
-
-        return;
-    }
-
-    /**
-     * DOMからIKチェーンリスト情報を組み立てる。
-     * @param pmdModelElem ルート要素
-     * @throws TogaXmlException 構文エラー
-     */
-    private void buildIkChainList(Element pmdModelElem)
-            throws TogaXmlException{
-        Element ikChainListElem =
-                getChild(pmdModelElem, "ikChainList");
-
-        List<IKChain> ikChainList = this.model.getIKChainList();
-
-        for(Element ikChainElem : eachChild(ikChainListElem, "ikChain")){
-            IKChain ikChain = new IKChain();
-            ikChainList.add(ikChain);
-
-            String ikBoneIdRef = getStringAttr(ikChainElem, "ikBoneIdRef");
-            int rucursiveDepth =
-                    getIntegerAttr(ikChainElem, "recursiveDepth");
-            float weight = getFloatAttr(ikChainElem, "weight");
-
-            BoneInfo ikBone = this.boneMap.get(ikBoneIdRef);
-            ikChain.setIkBone(ikBone);
-            ikChain.setIKDepth(rucursiveDepth);
-            ikChain.setIKWeight(weight);
-
-            List<BoneInfo> chainList = ikChain.getChainedBoneList();
-
-            for(Element orderElem : eachChild(ikChainElem, "chainOrder")){
-                String boneIdRef = getStringAttr(orderElem, "boneIdRef");
-                BoneInfo chaindBone = this.boneMap.get(boneIdRef);
-                chainList.add(chaindBone);
-            }
-        }
-
-        return;
-    }
-
-    /**
-     * DOMからモーフリスト情報を組み立てる。
-     * @param pmdModelElem ルート要素
-     * @throws TogaXmlException 構文エラー
-     */
-    private void buildMorphList(Element pmdModelElem)
-            throws TogaXmlException{
-        Element morphListElem =
-                getChild(pmdModelElem, "morphList");
-
-        Map<MorphType, List<MorphPart>> morphMap = this.model.getMorphMap();
-
-        for(Element morphElem : eachChild(morphListElem, "morph")){
-            MorphPart morphPart = new MorphPart();
-
-            I18nText name = morphPart.getMorphName();
-            buildI18nName(morphElem, name);
-
-            String type = getStringAttr(morphElem, "type");
-            MorphType morphType = MorphType.valueOf(type);
-            morphPart.setMorphType(morphType);
-
-            List<MorphVertex> morphVertexList =
-                    morphPart.getMorphVertexList();
-
-            for(Element morphVertexElem
-                    : eachChild(morphElem, "morphVertex")){
-                String vtxIdRef = getStringAttr(morphVertexElem, "vtxIdRef");
-                Vertex baseVertex = this.vertexMap.get(vtxIdRef);
-                float xOff = getFloatAttr(morphVertexElem, "xOff");
-                float yOff = getFloatAttr(morphVertexElem, "yOff");
-                float zOff = getFloatAttr(morphVertexElem, "zOff");
-
-                MorphVertex morphVertex = new MorphVertex();
-                morphVertex.setBaseVertex(baseVertex);
-                MkPos3D position = morphVertex.getOffset();
-                position.setXpos(xOff);
-                position.setYpos(yOff);
-                position.setZpos(zOff);
-
-                morphVertexList.add(morphVertex);
-            }
-
-            morphMap.get(morphType).add(morphPart);
-        }
-
-        List<MorphPart> serialList = new LinkedList<MorphPart>();
-        MorphPart baseDummy = new MorphPart();
-        serialList.add(baseDummy);
-        for(MorphPart part : morphMap.get(MorphType.EYEBROW)){
-            serialList.add(part);
-        }
-        for(MorphPart part : morphMap.get(MorphType.EYE)){
-            serialList.add(part);
-        }
-        for(MorphPart part : morphMap.get(MorphType.LIP)){
-            serialList.add(part);
-        }
-        for(MorphPart part : morphMap.get(MorphType.EXTRA)){
-            serialList.add(part);
-        }
-        ListUtil.assignIndexedSerial(serialList);
-
-        return;
-    }
-
-    /**
-     * DOMからボーングループ情報を組み立てる。
-     * @param pmdModelElem ルート要素
-     * @throws TogaXmlException 構文エラー
-     */
-    private void buildBoneGroupList(Element pmdModelElem)
-            throws TogaXmlException{
-        Element boneGroupListElem =
-                getChild(pmdModelElem, "boneGroupList");
-
-        List<BoneGroup> boneGroupList = this.model.getBoneGroupList();
-        BoneGroup defaultGroup = new BoneGroup();
-        boneGroupList.add(defaultGroup);
-
-        for(Element boneGroupElem
-                : eachChild(boneGroupListElem, "boneGroup")){
-            BoneGroup group = new BoneGroup();
-            boneGroupList.add(group);
-
-            I18nText name = group.getGroupName();
-            buildI18nName(boneGroupElem, name);
-
-            for(Element boneGroupMemberElem
-                    : eachChild(boneGroupElem, "boneGroupMember")){
-                String boneIdRef =
-                        getStringAttr(boneGroupMemberElem, "boneIdRef");
-                BoneInfo bone = this.boneMap.get(boneIdRef);
-                group.getBoneList().add(bone);
-            }
-        }
-
-        ListUtil.assignIndexedSerial(boneGroupList);
-
-        return;
-    }
-
-    /**
-     * DOMから剛体リスト情報を組み立てる。
-     * @param pmdModelElem ルート要素
-     * @throws TogaXmlException 構文エラー
-     */
-    private void buildRigidList(Element pmdModelElem)
-            throws TogaXmlException{
-        Element rigidListElem =
-                getChild(pmdModelElem, "rigidList");
-
-        List<RigidInfo> rigidList = this.model.getRigidList();
-
-        for(Element rigidElem : eachChild(rigidListElem, "rigid")){
-            RigidInfo rigid = new RigidInfo();
-            rigidList.add(rigid);
-
-            I18nText name = rigid.getRigidName();
-            buildI18nName(rigidElem, name);
-
-            String behavior = getStringAttr(rigidElem, "behavior");
-            RigidBehaviorType type = RigidBehaviorType.valueOf(behavior);
-            rigid.setBehaviorType(type);
-
-            String rigidId = getStringAttr(rigidElem, "rigidId");
-            this.rigidMap.put(rigidId, rigid);
-
-            if(hasChild(rigidElem, "linkedBone")){
-                Element linkedBoneElem = getChild(rigidElem, "linkedBone");
-                String boneIdRef = getStringAttr(linkedBoneElem, "boneIdRef");
-                BoneInfo linkedBone = this.boneMap.get(boneIdRef);
-                rigid.setLinkedBone(linkedBone);
-            }
-
-            RigidShape rigidShape = rigid.getRigidShape();
-            if(hasChild(rigidElem, "rigidShapeSphere")){
-                Element shapeElem =
-                        getChild(rigidElem, "rigidShapeSphere");
-                float radius = getFloatAttr(shapeElem, "radius");
-                rigidShape.setShapeType(RigidShapeType.SPHERE);
-                rigidShape.setRadius(radius);
-            }
-            if(hasChild(rigidElem, "rigidShapeBox")){
-                Element shapeElem =
-                        getChild(rigidElem, "rigidShapeBox");
-                float width  = getFloatAttr(shapeElem, "width");
-                float height = getFloatAttr(shapeElem, "height");
-                float depth  = getFloatAttr(shapeElem, "depth");
-                rigidShape.setShapeType(RigidShapeType.BOX);
-                rigidShape.setWidth(width);
-                rigidShape.setHeight(height);
-                rigidShape.setDepth(depth);
-            }
-            if(hasChild(rigidElem, "rigidShapeCapsule")){
-                Element shapeElem =
-                        getChild(rigidElem, "rigidShapeCapsule");
-                float height = getFloatAttr(shapeElem, "height");
-                float radius = getFloatAttr(shapeElem, "radius");
-                rigidShape.setShapeType(RigidShapeType.CAPSULE);
-                rigidShape.setHeight(height);
-                rigidShape.setRadius(radius);
-            }
-
-            float xVal;
-            float yVal;
-            float zVal;
-
-            Element positionElem = getChild(rigidElem, "position");
-            xVal = getFloatAttr(positionElem, "x");
-            yVal = getFloatAttr(positionElem, "y");
-            zVal = getFloatAttr(positionElem, "z");
-            MkPos3D position = rigid.getPosition();
-            position.setXpos(xVal);
-            position.setYpos(yVal);
-            position.setZpos(zVal);
-
-            Element radRotationElem = getChild(rigidElem, "radRotation");
-            xVal = getFloatAttr(radRotationElem, "xRad");
-            yVal = getFloatAttr(radRotationElem, "yRad");
-            zVal = getFloatAttr(radRotationElem, "zRad");
-            Rad3d rotation = rigid.getRotation();
-            rotation.setXRad(xVal);
-            rotation.setYRad(yVal);
-            rotation.setZRad(zVal);
-
-            Element dynamicsElem = getChild(rigidElem, "dynamics");
-            float mass = getFloatAttr(dynamicsElem, "mass");
-            float dampingPosition =
-                    getFloatAttr(dynamicsElem, "dampingPosition");
-            float dampingRotation =
-                    getFloatAttr(dynamicsElem, "dampingRotation");
-            float restitution = getFloatAttr(dynamicsElem, "restitution");
-            float friction = getFloatAttr(dynamicsElem, "friction");
-            DynamicsInfo dynamics = rigid.getDynamicsInfo();
-            dynamics.setMass(mass);
-            dynamics.setDampingPosition(dampingPosition);
-            dynamics.setDampingRotation(dampingRotation);
-            dynamics.setRestitution(restitution);
-            dynamics.setFriction(friction);
-        }
-
-        ListUtil.assignIndexedSerial(rigidList);
-
-        return;
-    }
-
-    /**
-     * DOMから剛体グループリスト情報を組み立てる。
-     * @param pmdModelElem ルート要素
-     * @throws TogaXmlException 構文エラー
-     */
-    private void buildRigidGroupList(Element pmdModelElem)
-            throws TogaXmlException{
-        Element rigidGroupListElem =
-                getChild(pmdModelElem, "rigidGroupList");
-
-        List<RigidGroup> groupList = this.model.getRigidGroupList();
-
-        for(Element rigidGroupElem
-                : eachChild(rigidGroupListElem, "rigidGroup")){
-            RigidGroup rigidGroup = new RigidGroup();
-            groupList.add(rigidGroup);
-
-            String rigidGroupId =
-                    getStringAttr(rigidGroupElem, "rigidGroupId");
-            this.rigidGroupMap.put(rigidGroupId, rigidGroup);
-
-            for(Element memberElem
-                    : eachChild(rigidGroupElem, "rigidGroupMember")){
-                String rigidIdRef = getStringAttr(memberElem, "rigidIdRef");
-                RigidInfo rigid = this.rigidMap.get(rigidIdRef);
-                rigidGroup.getRigidList().add(rigid);
-                rigid.setRigidGroup(rigidGroup);
-            }
-        }
-
-        while(groupList.size() < 16){
-            RigidGroup rigidGroup = new RigidGroup();
-            groupList.add(rigidGroup);
-        }
-
-        ListUtil.assignIndexedSerial(groupList);
-
-        return;
-    }
-
-    /**
-     * DOM内の剛体衝突情報を解決する。
-     * @param pmdModelElem ルート要素
-     * @throws TogaXmlException 構文エラー
-     */
-    private void resolveThroughRigidGroup(Element pmdModelElem)
-            throws TogaXmlException{
-        Element rigidListElem =
-                getChild(pmdModelElem, "rigidList");
-
-        List<RigidInfo> rigidList = this.model.getRigidList();
-
-        Iterator<RigidInfo> rit = rigidList.iterator();
-        for(Element rigidElem : eachChild(rigidListElem, "rigid")){
-            RigidInfo rigid = rit.next();
-            for(Element groupElem
-                    : eachChild(rigidElem, "throughRigidGroup")){
-                String groupId = getStringAttr(groupElem, "rigidGroupIdRef");
-                RigidGroup group = this.rigidGroupMap.get(groupId);
-                rigid.getThroughGroupColl().add(group);
-            }
-        }
-
-        return;
-    }
-
-    /**
-     * DOMからジョイントリストを組み立てる。
-     * @param pmdModelElem ルート要素
-     * @throws TogaXmlException 構文エラー
-     */
-    private void buildJointList(Element pmdModelElem)
-            throws TogaXmlException{
-        Element jointListElem =
-                getChild(pmdModelElem, "jointList");
-
-        List<JointInfo> jointList = this.model.getJointList();
-
-        for(Element jointElem : eachChild(jointListElem, "joint")){
-            JointInfo joint = new JointInfo();
-            jointList.add(joint);
-
-            I18nText name = joint.getJointName();
-            buildI18nName(jointElem, name);
-
-            Element rigidPairElem = getChild(jointElem, "jointedRigidPair");
-            String rigidIdRef1 = getStringAttr(rigidPairElem, "rigidIdRef1");
-            String rigidIdRef2 = getStringAttr(rigidPairElem, "rigidIdRef2");
-            RigidInfo rigid1 = this.rigidMap.get(rigidIdRef1);
-            RigidInfo rigid2 = this.rigidMap.get(rigidIdRef2);
-            joint.setRigidPair(rigid1, rigid2);
-
-            float xVal;
-            float yVal;
-            float zVal;
-            float xFrom;
-            float xTo;
-            float yFrom;
-            float yTo;
-            float zFrom;
-            float zTo;
-
-            MkPos3D position = joint.getPosition();
-            Element positionElem = getChild(jointElem, "position");
-            xVal = getFloatAttr(positionElem, "x");
-            yVal = getFloatAttr(positionElem, "y");
-            zVal = getFloatAttr(positionElem, "z");
-            position.setXpos(xVal);
-            position.setYpos(yVal);
-            position.setZpos(zVal);
-
-            TripletRange limitPosition = joint.getPositionRange();
-            Element limitPositionElem = getChild(jointElem, "limitPosition");
-            xFrom = getFloatAttr(limitPositionElem, "xFrom");
-            xTo   = getFloatAttr(limitPositionElem, "xTo");
-            yFrom = getFloatAttr(limitPositionElem, "yFrom");
-            yTo   = getFloatAttr(limitPositionElem, "yTo");
-            zFrom = getFloatAttr(limitPositionElem, "zFrom");
-            zTo   = getFloatAttr(limitPositionElem, "zTo");
-            limitPosition.setXRange(xFrom, xTo);
-            limitPosition.setYRange(yFrom, yTo);
-            limitPosition.setZRange(zFrom, zTo);
-
-            Rad3d rotation = joint.getRotation();
-            Element rotationElem = getChild(jointElem, "radRotation");
-            xVal = getFloatAttr(rotationElem, "xRad");
-            yVal = getFloatAttr(rotationElem, "yRad");
-            zVal = getFloatAttr(rotationElem, "zRad");
-            rotation.setXRad(xVal);
-            rotation.setYRad(yVal);
-            rotation.setZRad(zVal);
-
-            TripletRange limitRotation = joint.getRotationRange();
-            Element limitRotationElem = getChild(jointElem, "limitRotation");
-            xFrom = getFloatAttr(limitRotationElem, "xFrom");
-            xTo   = getFloatAttr(limitRotationElem, "xTo");
-            yFrom = getFloatAttr(limitRotationElem, "yFrom");
-            yTo   = getFloatAttr(limitRotationElem, "yTo");
-            zFrom = getFloatAttr(limitRotationElem, "zFrom");
-            zTo   = getFloatAttr(limitRotationElem, "zTo");
-            limitRotation.setXRange(xFrom, xTo);
-            limitRotation.setYRange(yFrom, yTo);
-            limitRotation.setZRange(zFrom, zTo);
-
-            MkPos3D elasticPosition = joint.getElasticPosition();
-            Element elasticPositionElem =
-                    getChild(jointElem, "elasticPosition");
-            xVal = getFloatAttr(elasticPositionElem, "x");
-            yVal = getFloatAttr(elasticPositionElem, "y");
-            zVal = getFloatAttr(elasticPositionElem, "z");
-            elasticPosition.setXpos(xVal);
-            elasticPosition.setYpos(yVal);
-            elasticPosition.setZpos(zVal);
-
-            Deg3d elasticRotation = joint.getElasticRotation();
-            Element elasticRotationElem =
-                    getChild(jointElem, "elasticRotation");
-            xVal = getFloatAttr(elasticRotationElem, "xDeg");
-            yVal = getFloatAttr(elasticRotationElem, "yDeg");
-            zVal = getFloatAttr(elasticRotationElem, "zDeg");
-            elasticRotation.setXDeg(xVal);
-            elasticRotation.setYDeg(yVal);
-            elasticRotation.setZDeg(zVal);
-        }
-
-        return;
-    }
-
-}
index ae2c411..9375b97 100644 (file)
@@ -7,7 +7,7 @@
 
 package jp.sfjp.mikutoga.pmd2xml;
 
-import jp.sfjp.mikutoga.pmd.xml.XmlModelFileType;
+import jp.sfjp.mikutoga.pmd.model.xml.XmlModelFileType;
 
 /**
  * モデルファイル種別。
index 06d432c..92f103a 100644 (file)
@@ -22,7 +22,7 @@ import java.text.MessageFormat;
 import java.util.Properties;
 import jp.sfjp.mikutoga.bin.parser.MmdFormatException;
 import jp.sfjp.mikutoga.pmd.IllegalPmdDataException;
-import jp.sourceforge.mikutoga.xml.TogaXmlException;
+import jp.sfjp.mikutoga.xml.TogaXmlException;
 import org.xml.sax.SAXException;
 
 /**
index 07a24bb..f85f928 100644 (file)
@@ -20,21 +20,22 @@ import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.validation.Schema;
 import jp.sfjp.mikutoga.bin.parser.MmdFormatException;
 import jp.sfjp.mikutoga.pmd.IllegalPmdDataException;
-import jp.sfjp.mikutoga.pmd.binio.PmdExporter;
-import jp.sfjp.mikutoga.pmd.binio.PmdLoader;
 import jp.sfjp.mikutoga.pmd.model.PmdModel;
-import jp.sfjp.mikutoga.pmd.xml.PmdXmlExporter;
-import jp.sfjp.mikutoga.pmd.xml.Schema101009;
-import jp.sfjp.mikutoga.pmd.xml.Schema130128;
-import jp.sfjp.mikutoga.pmd.xml.XmlLoader;
-import jp.sfjp.mikutoga.pmd.xml.XmlModelFileType;
-import jp.sourceforge.mikutoga.xml.BotherHandler;
-import jp.sourceforge.mikutoga.xml.LocalXmlResource;
-import jp.sourceforge.mikutoga.xml.SchemaUtil;
-import jp.sourceforge.mikutoga.xml.TogaXmlException;
-import jp.sourceforge.mikutoga.xml.XmlResourceResolver;
+import jp.sfjp.mikutoga.pmd.model.binio.PmdExporter;
+import jp.sfjp.mikutoga.pmd.model.binio.PmdLoader;
+import jp.sfjp.mikutoga.pmd.model.xml.PmdXmlExporter;
+import jp.sfjp.mikutoga.pmd.model.xml.Schema101009;
+import jp.sfjp.mikutoga.pmd.model.xml.Schema130128;
+import jp.sfjp.mikutoga.pmd.model.xml.XmlModelFileType;
+import jp.sfjp.mikutoga.pmd.model.xml.XmlPmdLoader;
+import jp.sfjp.mikutoga.xml.BotherHandler;
+import jp.sfjp.mikutoga.xml.LocalXmlResource;
+import jp.sfjp.mikutoga.xml.SchemaUtil;
+import jp.sfjp.mikutoga.xml.TogaXmlException;
+import jp.sfjp.mikutoga.xml.XmlResourceResolver;
 import org.xml.sax.InputSource;
 import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
 
 /**
  * PMD-XML間コンバータ本体。
@@ -320,9 +321,9 @@ public class Pmd2XmlConv {
             throws IOException,
                    SAXException,
                    TogaXmlException {
-        DocumentBuilder builder = buildBuilder();
-        XmlLoader loader = new XmlLoader();
-        PmdModel model = loader.parse(builder, source);
+        XMLReader reader = XmlInputUtil.buildReader(this.inTypes);
+        XmlPmdLoader loader = new XmlPmdLoader(reader);
+        PmdModel model = loader.parse(source);
         return model;
     }
 
@@ -361,7 +362,7 @@ public class Pmd2XmlConv {
         writer = new OutputStreamWriter(ostream, CS_UTF8);
         writer = new BufferedWriter(writer);
 
-        exporter.putPmdModel(model, writer);
+        exporter.putPmdXml(model, writer);
 
         exporter.close();
 
diff --git a/src/main/java/jp/sfjp/mikutoga/pmd2xml/XmlInputUtil.java b/src/main/java/jp/sfjp/mikutoga/pmd2xml/XmlInputUtil.java
new file mode 100644 (file)
index 0000000..0772100
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * xml input utility
+ *
+ * License : The MIT License
+ * Copyright(c) 2013 MikuToga Partners
+ */
+
+package jp.sfjp.mikutoga.pmd2xml;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.validation.Schema;
+import jp.sfjp.mikutoga.pmd.model.xml.Schema101009;
+import jp.sfjp.mikutoga.pmd.model.xml.Schema130128;
+import jp.sfjp.mikutoga.xml.BotherHandler;
+import jp.sfjp.mikutoga.xml.LocalXmlResource;
+import jp.sfjp.mikutoga.xml.SchemaUtil;
+import jp.sfjp.mikutoga.xml.XmlResourceResolver;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+
+/**
+ * XML入力に関する各種ユーティリティ。
+ */
+class XmlInputUtil {
+
+    /**
+     * 隠しコンストラクタ。
+     */
+    private XmlInputUtil(){
+        assert false;
+        throw new AssertionError();
+    }
+
+
+    /**
+     * SAXパーサファクトリを生成する。
+     * <ul>
+     * <li>XML名前空間機能は有効になる。
+     * <li>DTDによる形式検証は無効となる。
+     * <li>XIncludeによる差し込み機能は無効となる。
+     * </ul>
+     * @param schema スキーマ
+     * @return ファクトリ
+     */
+    private static SAXParserFactory buildFactory(Schema schema){
+        SAXParserFactory factory = SAXParserFactory.newInstance();
+
+        factory.setNamespaceAware(true);
+        factory.setValidating(false);
+        factory.setXIncludeAware(false);
+//      factory.setFeature(name, value);
+
+        factory.setSchema(schema);
+
+        return factory;
+    }
+
+    /**
+     * SAXパーサを生成する。
+     * @param schema スキーマ
+     * @return SAXパーサ
+     */
+    private static SAXParser buildParser(Schema schema){
+        SAXParserFactory factory = buildFactory(schema);
+
+        SAXParser parser;
+        try{
+            parser = factory.newSAXParser();
+        }catch(ParserConfigurationException e){
+            assert false;
+            throw new AssertionError(e);
+        }catch(SAXException e){
+            assert false;
+            throw new AssertionError(e);
+        }
+
+//      parser.setProperty(name, value);
+
+        return parser;
+    }
+
+    /**
+     * スキーマを生成する。
+     * @param resolver リゾルバ
+     * @param xmlInType 入力XML種別
+     * @return スキーマ
+     */
+    private static Schema builsSchema(XmlResourceResolver resolver,
+                                        ModelFileType xmlInType ){
+        LocalXmlResource[] schemaArray;
+        switch(xmlInType){
+        case XML_101009:
+            schemaArray = new LocalXmlResource[]{
+                Schema101009.SINGLETON,
+            };
+            break;
+        case XML_130128:
+            schemaArray = new LocalXmlResource[]{
+                Schema130128.SINGLETON,
+            };
+            break;
+        case XML_AUTO:
+            schemaArray = new LocalXmlResource[]{
+                Schema101009.SINGLETON,
+                Schema130128.SINGLETON,
+            };
+            break;
+        default:
+            throw new IllegalStateException();
+        }
+
+        Schema schema = SchemaUtil.newSchema(resolver, schemaArray);
+
+        return schema;
+    }
+
+    /**
+     * XMLリーダを生成する。
+     * <p>エラーハンドラには{@link BotherHandler}が指定される。
+     * @param xmlInType 入力XML種別
+     * @return XMLリーダ
+     */
+    static XMLReader buildReader(ModelFileType xmlInType){
+        XmlResourceResolver resolver = new XmlResourceResolver();
+
+        Schema schema = builsSchema(resolver, xmlInType);
+
+        SAXParser parser = buildParser(schema);
+
+        XMLReader reader;
+        try{
+            reader = parser.getXMLReader();
+        }catch(SAXException e){
+            assert false;
+            throw new AssertionError(e);
+        }
+
+        reader.setEntityResolver(resolver);
+        reader.setErrorHandler(BotherHandler.HANDLER);
+
+        return reader;
+    }
+
+}
index 53a2dbb..8a20800 100644 (file)
@@ -3,7 +3,7 @@
 
 package jp.sfjp.mikutoga.pmd2xml;
 
-import jp.sfjp.mikutoga.pmd.xml.XmlModelFileType;
+import jp.sfjp.mikutoga.pmd.model.xml.XmlModelFileType;
 import org.junit.After;
 import org.junit.AfterClass;
 import org.junit.Before;
index 1d73e22..1932e57 100644 (file)
@@ -57,6 +57,7 @@ public class CnvAssert {
         OutputStream destOut;
         destOut = new FileOutputStream(destFile);
         destOut = new BufferedOutputStream(destOut);
+//        destOut = new DebugOutputStream(destOut);
 
         Pmd2XmlConv converter = new Pmd2XmlConv();
         converter.setInType(ModelFileType.XML_AUTO);
@@ -188,12 +189,18 @@ public class CnvAssert {
         InputStream expis = new BufferedInputStream(expIn);
         InputStream resis = new BufferedInputStream(resIn);
 
-
+        int offset = 0;
         for(;;){
             int expCh = expis.read();
             int resCh = resis.read();
 
-            assertEquals(expCh, resCh);
+            try{
+                assertEquals(expCh, resCh);
+            }catch(AssertionError e){
+                System.err.println("offset=" + offset);
+                throw e;
+            }
+            offset++;
 
             if(expCh < 0) break;
         }
diff --git a/src/test/java/testdata/DebugOutputStream.java b/src/test/java/testdata/DebugOutputStream.java
new file mode 100644 (file)
index 0000000..535f6d6
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ */
+
+package testdata;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * デバッガ監視用出力ストリーム。
+ */
+public class DebugOutputStream extends OutputStream{
+
+    private final OutputStream os;
+    private long offset = 0L;
+
+
+    /**
+     * コンストラクタ。
+     * @param os 委譲先出力ストリーム
+     */
+    public DebugOutputStream(OutputStream os){
+        super();
+        this.os = os;
+        return;
+    }
+
+
+    /**
+     * デバッガ用監視場所。
+     */
+    private void before(){
+        return;
+    }
+
+    /**
+     * デバッガ用監視場所。
+     */
+    private void after(){
+        this.offset++;
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param b {@inheritDoc}
+     * @throws IOException {@inheritDoc}
+     */
+    @Override
+    public void write(int b) throws IOException {
+        before();
+
+        this.os.write(b);
+
+        after();
+
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws IOException {@inheritDoc}
+     */
+    @Override
+    public void close() throws IOException {
+        this.os.close();
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws IOException {@inheritDoc}
+     */
+    @Override
+    public void flush() throws IOException {
+        this.os.flush();
+        return;
+    }
+
+}
index 9fa3b6e..32ba98a 100644 (file)
@@ -135,7 +135,7 @@ Rigid behavior types:
       friction="0.5"
     />
 
-    <throughRigidGroup rigidGroupIdRef="rg1"  />
+    <throughRigidGroup rigidGroupIdRef="rg1" />
   </rigid>
 
 </rigidList>
index 1b150b3..7777827 100644 (file)
@@ -135,7 +135,7 @@ Rigid behavior types:
       friction="0.5"
     />
 
-    <throughRigidGroup rigidGroupIdRef="rg1"  />
+    <throughRigidGroup rigidGroupIdRef="rg1" />
   </rigid>
 
 </rigidList>