OSDN Git Service

Upgrade ToaGem to 3.122.2
[mikutoga/Pmd2XML.git] / src / main / java / jp / sfjp / mikutoga / pmd / model / xml / PmdXmlExporter.java
1 /*
2  * pmd-xml exporter
3  *
4  * License : The MIT License
5  * Copyright(c) 2010 MikuToga Partners
6  */
7
8 package jp.sfjp.mikutoga.pmd.model.xml;
9
10 import java.io.IOException;
11 import jp.sfjp.mikutoga.corelib.I18nText;
12 import jp.sfjp.mikutoga.pmd.model.PmdModel;
13 import jp.sfjp.mikutoga.xml.BasicXmlExporter;
14 import jp.sfjp.mikutoga.xml.SchemaUtil;
15
16 /**
17  * PMDモーションデータをXMLへエクスポートする。
18  */
19 public class PmdXmlExporter extends BasicXmlExporter{
20
21     private static final String XML_VER = "1.0";
22     private static final String XML_ENC = "UTF-8";
23     private static final String XML_DECL =
24               "<?xml version=\"" + XML_VER
25             + "\" encoding=\"" + XML_ENC
26             + "\" ?>";
27
28     private static final String XSINS = "xsi";
29
30     private static final String TOP_COMMENT =
31               "\u0020\u0020"               + "MikuMikuDance\n"
32             + "\u0020\u0020\u0020\u0020" + "model-data(*.pmd) on XML";
33
34     /** 改行文字列 CR。 */
35     private static final String CR = "\r";       // 0x0d
36     /** 改行文字列 LF。 */
37     private static final String LF = "\n";       // 0x0a
38     /** 改行文字列 CRLF。 */
39     private static final String CRLF = CR + LF;  // 0x0d, 0x0a
40
41
42     private XmlModelFileType xmlType = XmlModelFileType.XML_130128;
43
44     private String generator = null;
45
46     private final ExporterMaterial materialExporter;
47     private final ExporterBone     boneExporter;
48     private final ExporterMorph    morphExporter;
49     private final ExporterDynamics dynamicsExporter;
50     private final ExporterShape    shapeExporter;
51
52     private final ExtraExporter exp;
53
54
55     /**
56      * コンストラクタ。
57      */
58     public PmdXmlExporter(){
59         super();
60
61         this.materialExporter = new ExporterMaterial(this);
62         this.boneExporter     = new ExporterBone(this);
63         this.morphExporter    = new ExporterMorph(this);
64         this.dynamicsExporter = new ExporterDynamics(this);
65         this.shapeExporter    = new ExporterShape(this);
66
67         this.exp = new ExtraExporter(this);
68
69         this.boneExporter.setXmlFileType(this.xmlType);
70
71         return;
72     }
73
74
75     /**
76      * 出力XMLファイル種別を返す。
77      * @return ファイル種別
78      */
79     public XmlModelFileType getXmlFileType(){
80         return this.xmlType;
81     }
82
83     /**
84      * 出力XMLファイル種別を設定する。
85      * @param type ファイル種別
86      */
87     public void setXmlFileType(XmlModelFileType type){
88         switch(type){
89         case XML_101009:
90         case XML_130128:
91             this.xmlType = type;
92             break;
93         case XML_AUTO:
94             this.xmlType = XmlModelFileType.XML_130128;
95             break;
96         default:
97             throw new IllegalArgumentException();
98         }
99
100         assert this.xmlType == XmlModelFileType.XML_101009
101             || this.xmlType == XmlModelFileType.XML_130128;
102
103         this.boneExporter.setXmlFileType(this.xmlType);
104
105         return;
106     }
107
108     /**
109      * Generatorメタ情報を設定する。
110      * @param generatorArg Generatorメタ情報。表示したくないときはnull
111      */
112     public void setGenerator(String generatorArg){
113         this.generator = generatorArg;
114         return;
115     }
116
117     /**
118      * Generatorメタ情報を返す。
119      * @return Generatorメタ情報。表示したくないときはnull
120      */
121     public String getGenerator(){
122         return this.generator;
123     }
124
125     /**
126      * PMDモデルデータをXML形式で出力する。
127      * @param model PMDモデルデータ
128      * @param xmlOut XML出力先
129      * @throws IOException 出力エラー
130      */
131     public void putPmdXml(PmdModel model, Appendable xmlOut)
132             throws IOException{
133         setAppendable(xmlOut);
134
135         try{
136             putPmdXmlImpl(model);
137         }finally{
138             flush();
139         }
140
141         return;
142     }
143
144     /**
145      * PMDモデルデータをXML形式で出力する。
146      * @param model PMDモデルデータ
147      * @throws IOException 出力エラー
148      */
149     private void putPmdXmlImpl(PmdModel model) throws IOException{
150         putPmdRootOpen(model);
151
152         putModelInfo(model);
153         putMetaInfo();
154
155         this.materialExporter.putMaterialList(model);
156         this.materialExporter.putToonMap(model);
157
158         this.boneExporter.putBoneList(model);
159         this.boneExporter.putBoneGroupList(model);
160         this.boneExporter.putIKChainList(model);
161
162         this.morphExporter.putMorphList(model);
163
164         this.dynamicsExporter.putRigidList(model);
165         this.dynamicsExporter.putRigidGroupList(model);
166         this.dynamicsExporter.putJointList(model);
167
168         this.shapeExporter.putSurfaceGroupList(model);
169         this.shapeExporter.putVertexList(model);
170
171         ind().putETag(PmdTag.PMD_MODEL.tag()).ln(2);
172         ind().putLineComment("EOF").ln();
173
174         return;
175     }
176
177     /**
178      * ルートタグ開始を出力する。
179      * @param model モデル情報
180      * @throws IOException 出力エラー
181      */
182     private void putPmdRootOpen(PmdModel model)
183             throws IOException{
184         ind().putRawText(XML_DECL).ln(2);
185         ind().putBlockComment(TOP_COMMENT).ln(2);
186
187         I18nText modelName = model.getModelName();
188         ind();
189         this.exp.putLocalNameComment(modelName);
190         ln();
191
192         ind().putOpenSTag(PmdTag.PMD_MODEL.tag()).ln();
193         pushNest();
194
195         putPmdRootAttr(model);
196
197         popNest();
198         putCloseSTag().ln(2);
199
200         return;
201     }
202
203     /**
204      * ルートタグ属性を出力する。
205      * @param model モデル情報
206      * @throws IOException 出力エラー
207      */
208     private void putPmdRootAttr(PmdModel model)
209             throws IOException{
210         String namespace;
211         String schemaUrl;
212         String schemaVer;
213
214         if(this.xmlType == XmlModelFileType.XML_101009){
215             namespace = Schema101009.NS_PMDXML;
216             schemaUrl = Schema101009.SCHEMA_PMDXML;
217             schemaVer = Schema101009.VER_PMDXML;
218         }else if(this.xmlType == XmlModelFileType.XML_130128){
219             namespace = Schema130128.NS_PMDXML;
220             schemaUrl = Schema130128.SCHEMA_PMDXML;
221             schemaVer = Schema130128.VER_PMDXML;
222         }else{
223             assert false;
224             throw new AssertionError();
225         }
226
227         ind().putAttr("xmlns", namespace).ln();
228         ind().putAttr("xmlns:" + XSINS, SchemaUtil.NS_XSD).ln();
229
230         ind().putRawText(XSINS).putRawText(":schemaLocation=")
231              .putRawCh('"');
232         putRawText(namespace).ln();
233         ind().sp(2).putRawText(schemaUrl)
234              .putRawCh('"').ln();
235
236         ind().putAttr(PmdAttr.SCHEMA_VERSION.attr(), schemaVer);
237         ln(2);
238
239         I18nText modelName = model.getModelName();
240         ind();
241         this.exp.putPrimaryNameAttr(PmdAttr.NAME, modelName);
242         ln();
243
244         return;
245     }
246
247     /**
248      * モデル基本情報を出力する。
249      * @param model モデル情報
250      * @return this本体
251      * @throws IOException 出力エラー
252      */
253     private PmdXmlExporter putModelInfo(PmdModel model)
254             throws IOException{
255         I18nText modelName = model.getModelName();
256         this.exp.putI18nName(modelName);
257         ln();
258
259         I18nText description = model.getDescription();
260         for(String lang639 : description.lang639CodeList()){
261             String descText = description.getI18nText(lang639);
262             putDescription(lang639, descText);
263             ln();
264         }
265
266         return this;
267     }
268
269     /**
270      * モデル詳細テキストを出力する。
271      * @param lang639 言語コード
272      * @param content 詳細内容
273      * @return this本体
274      * @throws IOException 出力エラー
275      */
276     private PmdXmlExporter putDescription(CharSequence lang639,
277                                               CharSequence content)
278             throws IOException{
279         String text = content.toString();
280         text = text.replace(CRLF, LF);
281         text = text.replace(CR,   LF);
282
283         ind().putOpenSTag(PmdTag.DESCRIPTION.tag());
284         if( ! I18nText.CODE639_PRIMARY.equals(lang639) ){
285             sp().putAttr(PmdAttr.LANG.attr(), lang639).sp();
286         }
287         putCloseSTag().ln();
288
289         putBRedContent(text);
290
291         ind().putETag(PmdTag.DESCRIPTION.tag()).ln();
292
293         if( !    ExtraExporter.hasOnlyBasicLatin(text)
294               && isBasicLatinOnlyOut() ){
295             putBlockComment(text);
296         }
297
298         return this;
299     }
300
301     /**
302      * break要素を含む要素内容を出力する。
303      * 必要に応じてXML定義済み実体文字が割り振られた文字、
304      * コントロールコード、および非BasicLatin文字がエスケープされる。
305      * \nはbrタグに変換される。
306      * @param content 内容
307      * @return this本体
308      * @throws IOException 出力エラー
309      */
310     private PmdXmlExporter putBRedContent(CharSequence content)
311             throws IOException{
312         int length = content.length();
313
314         int startPos = 0;
315
316         for(int idx = 0; idx < length; idx++){
317             char ch = content.charAt(idx);
318             if(ch == '\n'){
319                 CharSequence seq = content.subSequence(startPos, idx);
320                 putContent(seq).putRawText("<br/>").ln();
321                 startPos = idx + 1;
322             }
323         }
324
325         if(startPos < length){
326             CharSequence seq = content.subSequence(startPos, length);
327             putContent(seq).ln();
328         }
329
330         return this;
331     }
332
333     /**
334      * 各種メタ情報を出力する。
335      * @return this本体
336      * @throws IOException 出力エラー
337      */
338     private PmdXmlExporter putMetaInfo() throws IOException{
339         ind().putSimpleSTag(PmdTag.LICENSE.tag()).ln();
340         ind().putETag(PmdTag.LICENSE.tag()).ln(2);
341
342         ind().putSimpleSTag(PmdTag.CREDITS.tag()).ln();
343         ind().putETag(PmdTag.CREDITS.tag()).ln(2);
344
345         String genName = getGenerator();
346         if(genName != null){
347             ind().putOpenSTag(PmdTag.META.tag()).sp();
348             putAttr(PmdAttr.NAME.attr(), "generator").sp();
349             putAttr(PmdAttr.CONTENT.attr(), genName).sp();
350             putCloseEmpty().ln();
351         }
352
353         ind().putOpenSTag(PmdTag.META.tag()).sp();
354         putAttr(PmdAttr.NAME.attr(), "siteURL").sp();
355         putAttr(PmdAttr.CONTENT.attr(), "").sp();
356         putCloseEmpty().ln();
357
358         ind().putOpenSTag(PmdTag.META.tag()).sp();
359         putAttr(PmdAttr.NAME.attr(), "imageURL").sp();
360         putAttr(PmdAttr.CONTENT.attr(), "").sp();
361         putCloseEmpty().ln(2);
362
363         return this;
364     }
365
366 }