OSDN Git Service

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