OSDN Git Service

MMD Ver7.40 対応 新スキーマ開発開始
[mikutoga/Vmd2XML.git] / src / main / java / jp / sfjp / mikutoga / vmd2xml / Vmd2XmlConv.java
1 /*
2  * vmd 2 xml converter
3  *
4  * License : The MIT License
5  * Copyright(c) 2013 MikuToga Partners
6  */
7
8 package jp.sfjp.mikutoga.vmd2xml;
9
10 import java.io.BufferedInputStream;
11 import java.io.BufferedWriter;
12 import java.io.IOException;
13 import java.io.InputStream;
14 import java.io.OutputStream;
15 import java.io.OutputStreamWriter;
16 import java.io.Writer;
17 import java.net.URL;
18 import java.nio.charset.Charset;
19 import javax.xml.parsers.ParserConfigurationException;
20 import javax.xml.parsers.SAXParser;
21 import javax.xml.parsers.SAXParserFactory;
22 import javax.xml.validation.Schema;
23 import jp.sfjp.mikutoga.bin.parser.MmdFormatException;
24 import jp.sfjp.mikutoga.vmd.IllegalVmdDataException;
25 import jp.sfjp.mikutoga.vmd.model.VmdMotion;
26 import jp.sfjp.mikutoga.vmd.model.binio.VmdExporter;
27 import jp.sfjp.mikutoga.vmd.model.binio.VmdLoader;
28 import jp.sfjp.mikutoga.vmd.model.xml.Schema110820;
29 import jp.sfjp.mikutoga.vmd.model.xml.VmdXmlExporter;
30 import jp.sfjp.mikutoga.vmd.model.xml.XmlVmdLoader;
31 import jp.sourceforge.mikutoga.xml.BotherHandler;
32 import jp.sourceforge.mikutoga.xml.SchemaUtil;
33 import jp.sourceforge.mikutoga.xml.TogaXmlException;
34 import jp.sourceforge.mikutoga.xml.XmlResourceResolver;
35 import org.xml.sax.InputSource;
36 import org.xml.sax.SAXException;
37 import org.xml.sax.XMLReader;
38
39 /**
40  * VMD-XML間コンバータ本体。
41  */
42 public class Vmd2XmlConv {
43
44     /** デフォルトエンコーディング。 */
45     private static final Charset CS_UTF8 = Charset.forName("UTF-8");
46
47
48     private MotionFileType inTypes  = MotionFileType.NONE;
49     private MotionFileType outTypes = MotionFileType.NONE;
50     private String newLine = "\r\n";
51     private String generator = null;
52     private boolean isQuaternionMode = true;
53
54
55     /**
56      * コンストラクタ。
57      */
58     public Vmd2XmlConv(){
59         super();
60         return;
61     }
62
63
64     /**
65      * InputSourceからInputStreamを得る。
66      * <p>入力ソースには、少なくともバイトストリームか
67      * URL文字列(SystemId)のいずれかが設定されていなければならない。
68      * @param source 入力ソース
69      * @return 入力バイトストリーム
70      * @throws IllegalArgumentException 入力ソースの設定が足りない。
71      * @throws IOException 入力ソースにアクセス不能。
72      */
73     private static InputStream openInputSource(InputSource source)
74             throws IllegalArgumentException, IOException{
75         InputStream is;
76
77         is = source.getByteStream();
78
79         if(is == null){
80             String systemId = source.getSystemId();
81             if(systemId == null) throw new IllegalArgumentException();
82
83             URL url = new URL(systemId);
84             is = url.openStream();
85         }
86
87         is = new BufferedInputStream(is);
88
89         return is;
90     }
91
92     /**
93      * SAXパーサファクトリを生成する。
94      * <ul>
95      * <li>XML名前空間機能は有効になる。
96      * <li>DTDによる形式検証は無効となる。
97      * <li>XIncludeによる差し込み機能は無効となる。
98      * </ul>
99      * @param schema スキーマ
100      * @return ファクトリ
101      */
102     private static SAXParserFactory buildFactory(Schema schema){
103         SAXParserFactory factory = SAXParserFactory.newInstance();
104
105         factory.setNamespaceAware(true);
106         factory.setValidating(false);
107         factory.setXIncludeAware(false);
108 //      factory.setFeature(name, value);
109
110         factory.setSchema(schema);
111
112         return factory;
113     }
114
115     /**
116      * SAXパーサを生成する。
117      * @param schema スキーマ
118      * @return SAXパーサ
119      */
120     private static SAXParser buildParser(Schema schema){
121         SAXParserFactory factory = buildFactory(schema);
122
123         SAXParser parser;
124         try{
125             parser = factory.newSAXParser();
126         }catch(ParserConfigurationException e){
127             assert false;
128             throw new AssertionError(e);
129         }catch(SAXException e){
130             assert false;
131             throw new AssertionError(e);
132         }
133
134 //      parser.setProperty(name, value);
135
136         return parser;
137     }
138
139     /**
140      * XMLリーダを生成する。
141      * <p>エラーハンドラには{@link BotherHandler}が指定される。
142      * @param resolver リゾルバ
143      * @return XMLリーダ
144      */
145     private static XMLReader buildReader(XmlResourceResolver resolver){
146         Schema schema;
147         schema = SchemaUtil.newSchema(resolver, Schema110820.SINGLETON);
148
149         SAXParser parser = buildParser(schema);
150
151         XMLReader reader;
152         try{
153             reader = parser.getXMLReader();
154         }catch(SAXException e){
155             assert false;
156             throw new AssertionError(e);
157         }
158
159         reader.setEntityResolver(resolver);
160         reader.setErrorHandler(BotherHandler.HANDLER);
161
162         return reader;
163     }
164
165     /**
166      * 入力ファイル種別を設定する。
167      * @param type ファイル種別
168      * @throws IllegalArgumentException 具体的な種別を渡さなかった
169      */
170     public void setInType(MotionFileType type)
171             throws IllegalArgumentException {
172         if(type == null) throw new NullPointerException();
173         if(type == MotionFileType.NONE) throw new IllegalArgumentException();
174         this.inTypes = type;
175         return;
176     }
177
178     /**
179      * 入力ファイル種別を返す。
180      * @return ファイル種別
181      */
182     public MotionFileType getInTypes(){
183         return this.inTypes;
184     }
185
186     /**
187      * 出力ファイル種別を設定する。
188      * @param type ファイル種別
189      * @throws IllegalArgumentException 具体的な種別を渡さなかった
190      */
191     public void setOutType(MotionFileType type)
192             throws IllegalArgumentException {
193         if(type == null) throw new NullPointerException();
194         if(type == MotionFileType.NONE) throw new IllegalArgumentException();
195         this.outTypes = type;
196         return;
197     }
198
199     /**
200      * 出力ファイル種別を返す。
201      * @return ファイル種別
202      */
203     public MotionFileType getOutTypes(){
204         return this.outTypes;
205     }
206
207     /**
208      * XML出力用改行文字列を設定する。
209      * @param newline 改行文字
210      */
211     public void setNewline(String newline){
212         this.newLine = newline;
213         return;
214     }
215
216     /**
217      * XML出力用改行文字列を返す。
218      * @return 改行文字
219      */
220     public String getNewline(){
221         return this.newLine;
222     }
223
224     /**
225      * ジェネレータ名を設定する。
226      * @param generator ジェネレータ名。表示したくない場合はnull
227      */
228     public void setGenerator(String generator){
229         this.generator = generator;
230         return;
231     }
232
233     /**
234      * ジェネレータ名を返す。
235      * @return ジェネレータ名。非表示の場合はnullを返す。
236      */
237     public String getGenerator(){
238         return this.generator;
239     }
240
241     /**
242      * 回転情報をクォータニオン形式でXML出力するか設定する。
243      * @param sw クォータニオン形式ならtrue、
244      * YXZオイラー角で出力したければfalse。
245      */
246     public void setQuaterniomMode(boolean sw){
247         this.isQuaternionMode = sw;
248         return;
249     }
250
251     /**
252      * 回転情報のXML出力形式を得る。
253      * @return クォータニオン形式ならtrue、YXZオイラー角形式ならfalse。
254      */
255     public boolean isQuaterniomMode(){
256         return this.isQuaternionMode;
257     }
258
259     /**
260      * ファイル変換を行う。
261      * <p>XML入力の場合は{@link #convert(InputSource, OutputStream)}を
262      * 推奨する。
263      * @param is 入力ストリーム
264      * @param os 出力ストリーム
265      * @throws IOException 入力エラー
266      * @throws MmdFormatException フォーマットエラー
267      * @throws SAXException XMLエラー
268      * @throws TogaXmlException XMLエラー
269      * @throws IllegalVmdDataException 内部エラー
270      */
271     public void convert(InputStream is, OutputStream os)
272             throws IOException,
273                    MmdFormatException,
274                    SAXException,
275                    TogaXmlException,
276                    IllegalVmdDataException {
277         VmdMotion motion = readMotion(is);
278         motion.frameSort();
279         writeMotion(motion, os);
280         return;
281     }
282
283     /**
284      * ファイル変換を行う。
285      * <p>VMD入力の場合は{@link InputStream}に
286      * バイトストリームが直接設定されていなければならない。
287      * <p>XML入力の場合は{@link InputStream}に
288      * URL(systemId)のみの設定を推奨する。
289      * @param source 入力ソース
290      * @param os 出力ストリーム
291      * @throws IOException 入力エラー
292      * @throws MmdFormatException フォーマットエラー
293      * @throws SAXException XMLエラー
294      * @throws TogaXmlException XMLエラー
295      * @throws IllegalVmdDataException 内部エラー
296      */
297     public void convert(InputSource source, OutputStream os)
298             throws IOException,
299                    MmdFormatException,
300                    SAXException,
301                    TogaXmlException,
302                    IllegalVmdDataException {
303         VmdMotion motion = readMotion(source);
304         motion.frameSort();
305         writeMotion(motion, os);
306         return;
307     }
308
309     /**
310      * モーションファイルを読み込む。
311      * <p>XML読み込みの場合は、
312      * こちらより{@link #readMotion(InputSource)}版を推奨する。
313      * @param is 入力ストリーム
314      * @return モーションデータ
315      * @throws IOException 入力エラー
316      * @throws MmdFormatException フォーマットエラー
317      * @throws SAXException XMLエラー
318      * @throws TogaXmlException XMLエラー
319      */
320     public VmdMotion readMotion(InputStream is)
321             throws IOException,
322                    MmdFormatException,
323                    SAXException,
324                    TogaXmlException {
325         InputSource source = new InputSource(is);
326
327         VmdMotion motion;
328         try{
329             motion = readMotion(source);
330         }finally{
331             is.close();
332         }
333
334         return motion;
335     }
336
337     /**
338      * モーションファイルを読み込む。
339      * <p>VMD入力の場合は、{@link InputStream}に
340      * 納められたバイトストリームかSystemId-URLから読み込む。
341      * @param source 入力ソース
342      * @return モーションデータ
343      * @throws IOException 入力エラー
344      * @throws MmdFormatException フォーマットエラー
345      * @throws SAXException XMLエラー
346      * @throws TogaXmlException XMLエラー
347      */
348     public VmdMotion readMotion(InputSource source)
349             throws IOException,
350                    MmdFormatException,
351                    SAXException,
352                    TogaXmlException {
353         VmdMotion motion = null;
354
355         if(this.inTypes.isVmd()){
356             InputStream is = openInputSource(source);
357             try{
358                 motion = vmdRead(is);
359             }finally{
360                 is.close();
361             }
362         }else if(this.inTypes.isXml()){
363             motion = xmlRead(source);
364         }else{
365             throw new IllegalStateException();
366         }
367
368         return motion;
369     }
370
371     /**
372      * モーションファイルを出力する。
373      * @param motion モーションデータ
374      * @param os 出力ストリーム
375      * @throws IOException 出力エラー
376      * @throws IllegalVmdDataException データの不備
377      */
378     public void writeMotion(VmdMotion motion, OutputStream os)
379             throws IOException,
380                    IllegalVmdDataException {
381         if(this.outTypes.isVmd()){
382             vmdOut(motion, os);
383         }else if(this.outTypes.isXml()){
384             xmlOut(motion, os);
385         }else{
386             throw new IllegalStateException();
387         }
388
389         return;
390     }
391
392     /**
393      * VMDファイルからモーションデータを読み込む。
394      * <p>入力ストリームは最後に閉じられる。
395      * @param is 入力ストリーム
396      * @return モーションデータ
397      * @throws IOException 入力エラー
398      * @throws MmdFormatException 不正なVMDファイルフォーマット
399      */
400     private VmdMotion vmdRead(InputStream is)
401             throws IOException, MmdFormatException{
402         VmdLoader loader = new VmdLoader();
403
404         loader.setRedundantCheck(false);
405
406         VmdMotion motion = loader.load(is);
407
408         return motion;
409     }
410
411     /**
412      * XMLファイルからモーションデータを読み込む。
413      * @param source 入力ソース
414      * @return モーションデータ
415      * @throws IOException 入力エラー
416      * @throws SAXException XML構文エラー
417      * @throws TogaXmlException 不正なXMLデータ
418      */
419     private VmdMotion xmlRead(InputSource source)
420             throws IOException,
421                    SAXException,
422                    TogaXmlException {
423         XmlResourceResolver resolver = new XmlResourceResolver();
424         XMLReader reader = buildReader(resolver);
425         XmlVmdLoader loader = new XmlVmdLoader(reader);
426
427         VmdMotion motion = loader.parse(source);
428
429         return motion;
430     }
431
432     /**
433      * モーションデータをVMDファイルに出力する。
434      * @param motion モーションデータ
435      * @param ostream 出力ストリーム
436      * @throws IOException 出力エラー
437      * @throws IllegalVmdDataException 不正なモーションデータ
438      */
439     private void vmdOut(VmdMotion motion, OutputStream ostream)
440             throws IOException, IllegalVmdDataException{
441         VmdExporter exporter = new VmdExporter();
442         exporter.dumpVmdMotion(motion, ostream);
443         ostream.close();
444         return;
445     }
446
447     /**
448      * モーションデータをXMLファイルに出力する。
449      * @param motion モーションデータ
450      * @param ostream 出力ストリーム
451      * @throws IOException 出力エラー
452      * @throws IllegalVmdDataException 不正なモーションデータ
453      */
454     private void xmlOut(VmdMotion motion, OutputStream ostream)
455             throws IOException, IllegalVmdDataException{
456         VmdXmlExporter exporter = new VmdXmlExporter();
457
458         exporter.setNewLine(this.newLine);
459         exporter.setGenerator(this.generator);
460         exporter.setQuaternionMode(this.isQuaternionMode);
461
462         Writer writer;
463         writer = new OutputStreamWriter(ostream, CS_UTF8);
464         writer = new BufferedWriter(writer);
465
466         exporter.putVmdXml(motion, writer);
467
468         exporter.close();
469
470         return;
471     }
472
473 }