OSDN Git Service

MmdInputStream廃止
[mikutoga/Vmd2XML.git] / src / main / java / jp / sourceforge / mikutoga / vmd2xml / Vmd2Xml.java
1 /*
2  * vmd to xml converter main entry
3  *
4  * License : The MIT License
5  * Copyright(c) 2011 MikuToga Partners
6  */
7
8 package jp.sourceforge.mikutoga.vmd2xml;
9
10 import java.io.BufferedInputStream;
11 import java.io.BufferedOutputStream;
12 import java.io.File;
13 import java.io.FileInputStream;
14 import java.io.FileNotFoundException;
15 import java.io.FileOutputStream;
16 import java.io.IOException;
17 import java.io.InputStream;
18 import java.io.OutputStream;
19 import java.text.MessageFormat;
20 import java.util.Properties;
21 import javax.xml.parsers.DocumentBuilder;
22 import jp.sourceforge.mikutoga.binio.FileUtils;
23 import jp.sourceforge.mikutoga.parser.MmdFormatException;
24 import jp.sourceforge.mikutoga.vmd.IllegalVmdDataException;
25 import jp.sourceforge.mikutoga.vmd.model.VmdMotion;
26 import jp.sourceforge.mikutoga.vmd.model.binio.VmdExporter;
27 import jp.sourceforge.mikutoga.vmd.model.binio.VmdLoader;
28 import jp.sourceforge.mikutoga.vmd.model.xml.VmdXmlExporter;
29 import jp.sourceforge.mikutoga.vmd.model.xml.VmdXmlResources;
30 import jp.sourceforge.mikutoga.vmd.model.xml.Xml2VmdLoader;
31 import jp.sourceforge.mikutoga.xml.TogaXmlException;
32 import org.xml.sax.InputSource;
33 import org.xml.sax.SAXException;
34 import org.xml.sax.SAXParseException;
35
36
37 /**
38  * VMDモーションファイルとXMLとの間で変換を行うアプリケーション。
39  */
40 public final class Vmd2Xml {
41
42     private static final Class<?> THISCLASS;
43     private static final String APPNAME;
44     private static final String APPVER;
45     private static final String APPLICENSE;
46     private static final String APPURL;
47
48     private static final int EXIT_OK     = 0;
49     private static final int EXIT_IOERR  = 1;
50     private static final int EXIT_ENVERR = 4;
51     private static final int EXIT_OPTERR = 5;
52     private static final int EXIT_INTERR = 6;
53     private static final int EXIT_VMDERR = 7;
54     private static final int EXIT_XMLERR = 8;
55
56     private static final String ERRMSG_TXTONLY =
57             "ERROR : {0}\n";
58     private static final String ERRMSG_SAXPARSE =
59             "ERROR : {0}\nline={1}, columun={2}\n";
60
61     static{
62         THISCLASS = Vmd2Xml.class;
63         InputStream verDef =
64                 THISCLASS.getResourceAsStream("resources/version.properties");
65         Properties verProps = new Properties();
66         try{
67             try{
68                 verProps.load(verDef);
69             }finally{
70                 verDef.close();
71             }
72         }catch(IOException e){
73             throw new ExceptionInInitializerError(e);
74         }
75
76         APPNAME    = verProps.getProperty("app.name");
77         APPVER     = verProps.getProperty("app.version");
78         APPLICENSE = verProps.getProperty("app.license");
79         APPURL     = verProps.getProperty("app.url");
80
81         new Vmd2Xml().hashCode();
82     }
83
84
85     /**
86      * 隠しコンストラクタ。
87      */
88     private Vmd2Xml(){
89         super();
90         assert this.getClass().equals(THISCLASS);
91         return;
92     }
93
94
95     /**
96      * Mainエントリ。
97      * @param args コマンドパラメータ
98      */
99     public static void main(String[] args){
100         checkJRE();
101
102         ArgInfo argInfo = ArgInfo.buildArgInfo(args);
103         if(argInfo.hasOptionError()){
104             String message = argInfo.getErrorMessage() + "\n"
105                     + "(-h for help)\n";
106             errExit(EXIT_OPTERR, message);
107         }
108
109         if(argInfo.isHelpMode()){
110             putHelp();
111             exit(EXIT_OK);
112         }
113
114         checkFiles(argInfo);
115
116         String input  = argInfo.getInputFile();
117         String output = argInfo.getOutputFile();
118
119         InputStream is = null;
120         OutputStream os = null;
121         try{
122             is = openInputStream(input);
123             os = openTruncatedOutputStream(output);
124         }catch(FileNotFoundException e){
125             ioError(e);
126         }catch(IOException e){
127             ioError(e);
128         }
129
130         if(argInfo.isVmd2XmlMode()) vmd2xml(is, os, argInfo);
131         else                        xml2vmd(is, os);
132
133         exit(EXIT_OK);
134
135         return;
136     }
137
138     /**
139      * JREのバージョン判定を行う。
140      * 不適切ならVMごと終了。
141      */
142     private static void checkJRE(){
143         Package jrePackage = java.lang.Object.class.getPackage();
144         if( ! jrePackage.isCompatibleWith("1.6")){
145             errExit(EXIT_ENVERR, "You need JRE 1.6 or later.\n");
146         }
147         return;
148     }
149
150     /**
151      * ヘルプメッセージを出力する。
152      */
153     private static void putHelp(){
154         StringBuilder text = new StringBuilder();
155
156         text.append(APPNAME).append(' ').append(APPVER).append('\n');
157         text.append("  License : ").append(APPLICENSE).append('\n');
158         text.append("  ").append(APPURL).append('\n');
159         text.append('\n');
160         text.append(ArgInfo.CMD_HELP);
161
162         errprint(text);
163
164         return;
165     }
166
167     /**
168      * ファイルの各種状態を事前にチェックする。
169      * @param argInfo コマンドライン引数
170      */
171     private static void checkFiles(ArgInfo argInfo){
172         String input  = argInfo.getInputFile();
173         String output = argInfo.getOutputFile();
174         File iFile = new File(input);
175         File oFile = new File(output);
176
177         if( ! FileUtils.isExistsNormalFile(iFile) ){
178             errExit(EXIT_IOERR, "Can't find input file:"
179                     + iFile.getAbsolutePath() + '\n');
180         }
181
182         if(argInfo.isForceMode()){
183             if(FileUtils.isExistsUnnormalFile(oFile)){
184                 errExit(EXIT_IOERR, oFile.getAbsolutePath()
185                         + " is not file.\n");
186             }
187         }else if(oFile.exists()){
188             errExit(EXIT_IOERR, oFile.getAbsolutePath()
189                     + " already exists.\n"
190                     + "If you want to overwrite, use -f.\n");
191         }
192
193         return;
194     }
195
196     /**
197      * VMD->XML変換を行う。
198      * @param istream 入力ストリーム
199      * @param ostream 出力ストリーム
200      * @param argInfo オプション設定
201      */
202     private static void vmd2xml(InputStream istream, OutputStream ostream,
203                                 ArgInfo argInfo ){
204         try{
205             vmd2xmlImpl(istream, ostream, argInfo);
206         }catch(IOException e){
207             ioError(e);
208         }catch(MmdFormatException e){
209             vmdError(e);
210         }catch(IllegalVmdDataException e){
211             internalError(e);
212         }
213
214         return;
215     }
216
217     /**
218      * VMD->XML変換を行う。
219      * @param istream 入力ストリーム
220      * @param ostream 出力ストリーム
221      * @param argInfo オプション設定
222      * @throws IOException 入出力エラー
223      * @throws MmdFormatException 不正なVMDファイル
224      * @throws IllegalVmdDataException 不正なモーションデータ
225      */
226     private static void vmd2xmlImpl(InputStream istream, OutputStream ostream,
227                                     ArgInfo argInfo )
228             throws IOException, MmdFormatException, IllegalVmdDataException{
229         VmdMotion motion;
230         try{
231             motion = vmdRead(istream);
232         }finally{
233             istream.close();
234         }
235
236         motion.frameSort();
237
238         try{
239             xmlOut(motion, ostream, argInfo);
240         }finally{
241             ostream.close();
242         }
243
244         return;
245     }
246
247     /**
248      * XML->VMD変換を行う。
249      * @param istream 入力ストリーム
250      * @param ostream 出力ストリーム
251      */
252     private static void xml2vmd(InputStream istream, OutputStream ostream){
253         try{
254             xml2vmdImpl(istream, ostream);
255         }catch(IOException e){
256             ioError(e);
257         }catch(SAXException e){
258             xmlError(e);
259         }catch(TogaXmlException e){
260             xmlError(e);
261         }catch(IllegalVmdDataException e){
262             internalError(e);
263         }
264
265         return;
266     }
267
268     /**
269      * XML->VMD変換を行う。
270      * @param istream 入力ストリーム
271      * @param ostream 出力ストリーム
272      * @throws IOException 入出力エラー
273      * @throws SAXException 不正なXMLファイル
274      * @throws TogaXmlException 不正なXMLファイル
275      * @throws IllegalVmdDataException 不正なVMDモーションデータ
276      */
277     private static void xml2vmdImpl(InputStream istream, OutputStream ostream)
278             throws IOException,
279                    SAXException,
280                    TogaXmlException,
281                    IllegalVmdDataException {
282         InputSource source = new InputSource(istream);
283         VmdMotion motion;
284         try{
285             motion = xmlRead(source);
286         }finally{
287             istream.close();
288         }
289
290         motion.frameSort();
291
292         try{
293             vmdOut(motion, ostream);
294         }finally{
295             ostream.close();
296         }
297
298         return;
299     }
300
301     /**
302      * VMDファイルからモーションデータを読み込む。
303      * @param is 入力ストリーム
304      * @return モーションデータ
305      * @throws IOException 入力エラー
306      * @throws MmdFormatException 不正なVMDファイルフォーマット
307      */
308     private static VmdMotion vmdRead(InputStream is)
309             throws IOException, MmdFormatException{
310         VmdMotion vmdMotion = VmdLoader.load(is);
311         return vmdMotion;
312     }
313
314     /**
315      * モーションデータをXMLファイルに出力する。
316      * @param motion モーションデータ
317      * @param ostream 出力ストリーム
318      * @param argInfo オプション設定
319      * @throws IOException 出力エラー
320      * @throws IllegalVmdDataException 不正なモーションデータ
321      */
322     private static void xmlOut(VmdMotion motion, OutputStream ostream,
323                                ArgInfo argInfo)
324             throws IOException, IllegalVmdDataException{
325         VmdXmlExporter exporter = new VmdXmlExporter(ostream);
326
327         exporter.setNewLine("\r\n");
328         exporter.setGenerator(APPNAME + ' ' + APPVER);
329
330         boolean isQuaternionMode = argInfo.isQuaternionMode();
331         exporter.setQuaternionMode(isQuaternionMode);
332
333         exporter.putVmdXml(motion);
334         exporter.close();
335
336         return;
337     }
338
339     /**
340      * XMLファイルからモーションデータを読み込む。
341      * @param source 入力ソース
342      * @return モーションデータ
343      * @throws IOException 入力エラー
344      * @throws SAXException XML構文エラー
345      * @throws TogaXmlException 不正なXMLデータ
346      */
347     private static VmdMotion xmlRead(InputSource source)
348             throws IOException,
349                    SAXException,
350                    TogaXmlException {
351         DocumentBuilder builder =
352                 VmdXmlResources.newBuilder(ValidationHandler.HANDLER);
353         Xml2VmdLoader loader = new Xml2VmdLoader(builder);
354
355         VmdMotion motion = loader.parse(source);
356
357         return motion;
358     }
359
360     /**
361      * モーションデータをVMDファイルに出力する。
362      * @param motion モーションデータ
363      * @param ostream 出力ストリーム
364      * @throws IOException 出力エラー
365      * @throws IllegalVmdDataException 不正なモーションデータ
366      */
367     private static void vmdOut(VmdMotion motion, OutputStream ostream)
368             throws IOException, IllegalVmdDataException{
369         VmdExporter exporter = new VmdExporter(ostream);
370         exporter.dumpVmdMotion(motion);
371         ostream.close();
372         return;
373     }
374
375     /**
376      * 入力ストリームを得る。
377      * @param fileName 入力ファイル名
378      * @return 入力ストリーム
379      * @throws FileNotFoundException 入力ファイルが見つからない。
380      */
381     private static InputStream openInputStream(String fileName)
382             throws FileNotFoundException {
383         File file = new File(fileName);
384         InputStream result = new FileInputStream(file);
385         result = new BufferedInputStream(result);
386         return result;
387     }
388
389     /**
390      * 出力ストリームを得る。
391      * @param fileName 出力ファイル名
392      * @return 出力ストリーム
393      * @throws FileNotFoundException 出力ファイルが見つからない
394      * @throws IOException 出力エラー
395      */
396     private static OutputStream openTruncatedOutputStream(String fileName)
397             throws FileNotFoundException, IOException {
398         File file = new File(fileName);
399         FileUtils.trunc(file);
400         OutputStream result = new FileOutputStream(file, false);
401         result = new BufferedOutputStream(result);
402         return result;
403     }
404
405     /**
406      * 入出力エラー処理。
407      * 例外を出力してVM終了する。
408      * @param ex 例外
409      */
410     private static void ioError(IOException ex){
411         errprint(ex);
412         errprint('\n');
413         exit(EXIT_IOERR);
414
415         return;
416     }
417
418     /**
419      * 内部エラー処理。
420      * 例外を出力してVM終了する。
421      * @param ex 例外
422      */
423     private static void internalError(Throwable ex){
424         errprint(ex);
425         errprint('\n');
426         ex.printStackTrace(System.err);
427         exit(EXIT_INTERR);
428
429         return;
430     }
431
432     /**
433      * VMDファイルフォーマットエラー処理。
434      * 例外を出力してVM終了する。
435      * @param ex 例外
436      */
437     private static void vmdError(MmdFormatException ex){
438         errprint(ex);
439         errprint('\n');
440         ex.printStackTrace(System.err);
441         exit(EXIT_VMDERR);
442
443         return;
444     }
445
446     /**
447      * XML構文エラー処理。
448      * 例外を出力してVM終了する。
449      * @param ex 例外
450      */
451     private static void xmlError(SAXException ex){
452         if(ex instanceof SAXParseException){
453             xmlError((SAXParseException)ex);
454         }
455
456         String txt = ex.getLocalizedMessage();
457         String message = MessageFormat.format(ERRMSG_TXTONLY, txt);
458         errprint(message);
459
460         exit(EXIT_XMLERR);
461
462         return;
463     }
464
465     /**
466      * XML構文エラー処理。
467      * 例外を出力してVM終了する。
468      * @param ex 例外
469      */
470     private static void xmlError(SAXParseException ex){
471         String txt = ex.getLocalizedMessage();
472         int line = ex.getLineNumber();
473         int col = ex.getColumnNumber();
474
475         String message =
476                 MessageFormat.format(ERRMSG_SAXPARSE, txt, line, col);
477         errprint(message);
478
479         exit(EXIT_XMLERR);
480
481         return;
482     }
483
484     /**
485      * XML構文エラー処理。
486      * 例外を出力してVM終了する。
487      * @param ex 例外
488      */
489     private static void xmlError(TogaXmlException ex){
490         String txt = ex.getLocalizedMessage();
491         String message = MessageFormat.format(ERRMSG_TXTONLY, txt);
492         errprint(message);
493
494         exit(EXIT_XMLERR);
495
496         return;
497     }
498
499     /**
500      * VMを終了する。
501      * @param code 終了コード
502      */
503     private static void exit(int code){
504         System.exit(code);
505         assert false;
506         throw new AssertionError();
507     }
508
509     /**
510      * 標準エラー出力に文字列出力を行う。
511      * @param obj 文字列
512      */
513     @SuppressWarnings("PMD.SystemPrintln")
514     private static void errprint(Object obj){
515         System.err.print(obj.toString());
516         return;
517     }
518
519     /**
520      * エラーを表示した後VMを終了させる。
521      * @param code 終了コード
522      * @param text メッセージ
523      */
524     private static void errExit(int code, CharSequence text){
525         errprint("ERROR:\n");
526         errprint(text);
527         exit(code);
528         return;
529     }
530
531 }