OSDN Git Service

SAXパーサ対応
[mikutoga/Vmd2XML.git] / src / main / java / jp / sfjp / 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.sfjp.mikutoga.vmd2xml;
9
10 import java.io.BufferedOutputStream;
11 import java.io.File;
12 import java.io.FileNotFoundException;
13 import java.io.FileOutputStream;
14 import java.io.IOException;
15 import java.io.InputStream;
16 import java.io.OutputStream;
17 import java.io.PrintStream;
18 import java.net.MalformedURLException;
19 import java.net.URI;
20 import java.net.URL;
21 import java.nio.channels.FileChannel;
22 import java.text.MessageFormat;
23 import java.util.Properties;
24 import jp.sfjp.mikutoga.bin.parser.MmdFormatException;
25 import jp.sfjp.mikutoga.vmd.IllegalVmdDataException;
26 import jp.sourceforge.mikutoga.xml.TogaXmlException;
27 import org.xml.sax.InputSource;
28 import org.xml.sax.SAXException;
29
30
31 /**
32  * VMDモーションファイルとXMLとの間で変換を行うアプリケーション。
33  */
34 public final class Vmd2Xml {
35
36     /** 正常系。 */
37     public static final int EXIT_OK     = 0;
38     /** 内部エラー。 */
39     public static final int EXIT_INTERR = 1;
40     /** 実行環境に起因するエラー。 */
41     public static final int EXIT_ENVERR = 2;
42     /** オプション指定に起因するエラー。 */
43     public static final int EXIT_OPTERR = 3;
44     /** ファイル入出力に起因するエラー。 */
45     public static final int EXIT_IOERR  = 4;
46     /** XMLフォーマットに起因するエラー。 */
47     public static final int EXIT_XMLERR = 5;
48     /** VMDフォーマットに起因するエラー。 */
49     public static final int EXIT_VMDERR = 7;
50
51     /** アプリ名。 */
52     public static final String APPNAME;
53     /** バージョン識別子。 */
54     public static final String APPVER;
55     /** ライセンス種別。 */
56     public static final String APPLICENSE;
57     /** 開発元URL。 */
58     public static final String APPURL;
59     /** ジェネレータ名。 */
60     public static final String GENERATOR;
61
62     private static final Class<?> THISCLASS;
63     private static final String RES_VER = "resources/version.properties";
64
65     private static final PrintStream ERROUT = System.err;
66     private static final String MSG_ERR = "ERROR:\n{0}\n(-h for help)";
67     private static final String MSG_HELP =
68               "{0} {1}\n"
69             + "\u0020\u0020License\u0020:\u0020{2}\n"
70             + "\u0020\u0020{3}\n";
71     private static final String MSG_NOINFILE = "Can't find input file:{0}";
72     private static final String MSG_ABNFILE = "{0} is not file.";
73     private static final String MSG_OWOUTFILE =
74               "{0} already exists.\n"
75             + "If you want to overwrite, use -f.";
76
77     private static final String MSG_OLDJRE = "You need JRE {0} or later.";
78     private static final String REQUIRED_JRE = "1.6";
79
80     static{
81         THISCLASS = Vmd2Xml.class;
82         InputStream ver = THISCLASS.getResourceAsStream(RES_VER);
83         Properties verProps = new Properties();
84         try{
85             try{
86                 verProps.load(ver);
87             }finally{
88                 ver.close();
89             }
90         }catch(IOException e){
91             throw new ExceptionInInitializerError(e);
92         }
93
94         APPNAME    = verProps.getProperty("app.name");
95         APPVER     = verProps.getProperty("app.version");
96         APPLICENSE = verProps.getProperty("app.license");
97         APPURL     = verProps.getProperty("app.url");
98
99         GENERATOR = APPNAME + ' ' + APPVER;
100
101         new Vmd2Xml().hashCode();
102     }
103
104
105     /**
106      * ダミーコンストラクタ。
107      */
108     private Vmd2Xml(){
109         super();
110         assert this.getClass().equals(THISCLASS);
111         return;
112     }
113
114
115     /**
116      * VMを終了させる。
117      * @param code 終了コード
118      * @see java.lang.System#exit(int)
119      */
120     private static void exit(int code){
121         System.exit(code);
122         assert false;
123         throw new AssertionError();
124     }
125
126     /**
127      * 共通エラーメッセージを出力する。
128      * @param text 個別メッセージ
129      */
130     private static void errMsg(String text){
131         String msg = MessageFormat.format(MSG_ERR, text);
132         ERROUT.println(msg);
133         return;
134     }
135
136     /**
137      * 標準エラー出力へ例外情報出力。
138      * @param ex 例外
139      * @param dumpStack スタックトレースを出力するならtrue
140      */
141     private static void thPrintln(Throwable ex, boolean dumpStack){
142         if(dumpStack){
143             ex.printStackTrace(ERROUT);
144         }else{
145             String text = ex.toString();
146             ERROUT.println(text);
147         }
148
149         return;
150     }
151
152     /**
153      * 標準エラー出力へ例外情報出力。
154      * @param ex 例外
155      */
156     private static void thPrintln(Throwable ex){
157         thPrintln(ex, false);
158         return;
159     }
160
161     /**
162      * 入出力エラー処理。
163      * 例外を出力してVM終了する。
164      * @param ex 例外
165      */
166     private static void ioError(IOException ex){
167         thPrintln(ex);
168         exit(EXIT_IOERR);
169     }
170
171     /**
172      * XML構文エラー処理。
173      * 例外を出力してVM終了する。
174      * @param ex 例外
175      */
176     private static void xmlError(Throwable ex){
177         thPrintln(ex);
178         exit(EXIT_XMLERR);
179     }
180
181     /**
182      * VMDファイルフォーマットエラー処理。
183      * 例外を出力してVM終了する。
184      * @param ex 例外
185      */
186     private static void vmdError(MmdFormatException ex){
187         thPrintln(ex, true);
188         exit(EXIT_VMDERR);
189     }
190
191     /**
192      * 内部エラー処理。
193      * 例外を出力してVM終了する。
194      * @param ex 例外
195      */
196     private static void internalError(Throwable ex){
197         thPrintln(ex, true);
198         exit(EXIT_INTERR);
199     }
200
201     /**
202      * JREのバージョン判定を行う。
203      * 不適切ならVMごと終了。
204      */
205     private static void checkJRE(){
206         Package jrePackage = java.lang.Object.class.getPackage();
207         if( ! jrePackage.isCompatibleWith(REQUIRED_JRE)){
208             String msg = MessageFormat.format(MSG_OLDJRE, REQUIRED_JRE);
209             ERROUT.println(msg);
210             exit(EXIT_ENVERR);
211         }
212         return;
213     }
214
215     /**
216      * ヘルプメッセージを出力する。
217      */
218     private static void putHelp(){
219         String msg =
220                 MessageFormat.format(MSG_HELP,
221                 APPNAME, APPVER, APPLICENSE, APPURL);
222         ERROUT.println(msg);
223         ERROUT.println(OptSwitch.getConsoleHelp());
224         return;
225     }
226
227     /**
228      * ファイルサイズを0に切り詰める。
229      * <p>ファイルが存在しなければなにもしない。
230      * <p>通常ファイルでなければなにもしない。
231      * @param file ファイル
232      * @throws IOException 入出力エラー
233      */
234     private static void trunc(File file) throws IOException{
235         if( ! file.exists() ) return;
236         if( ! file.isFile() ) return;
237
238         if(file.length() <= 0L) return;
239
240         FileOutputStream foStream = new FileOutputStream(file);
241         try{
242             FileChannel channnel = foStream.getChannel();
243             try{
244                 channnel.truncate(0L);
245             }finally{
246                 channnel.close();
247             }
248         }finally{
249             foStream.close();
250         }
251
252         return;
253     }
254
255     /**
256      * 入力ソースを準備する。
257      * <p>入力ファイルが通常ファイルとして存在しなければエラー終了。
258      * @param optInfo オプション情報
259      * @return 入力ソース
260      */
261     private static InputSource openInfile(OptInfo optInfo){
262         String fileName = optInfo.getInFilename();
263         File inFile = new File(fileName);
264
265         if( (! inFile.exists()) || (! inFile.isFile()) ){
266             String absPath = inFile.getAbsolutePath();
267             String msg = MessageFormat.format(MSG_NOINFILE, absPath);
268             errMsg(msg);
269             exit(EXIT_IOERR);
270         }
271
272         URI uri = inFile.toURI();
273         URL url;
274         try{
275             url = uri.toURL();
276         }catch(MalformedURLException e){
277             // File由来のURLでは起こりえない
278             assert false;
279             throw new AssertionError(e);
280         }
281         String systemId = url.toString();
282
283         InputSource source = new InputSource(systemId);
284
285         return source;
286     }
287
288     /**
289      * 出力ストリームを準備する。
290      * <p>出力ファイルが通常ファイルでない場合はエラー終了。
291      * <p>既存の出力ファイルに上書き指示が伴っていなければエラー終了。
292      * @param optInfo オプション情報
293      * @return 出力ストリーム
294      */
295     private static OutputStream openOutfile(OptInfo optInfo) {
296         String outputFile = optInfo.getOutFilename();
297         boolean overwrite = optInfo.overwriteMode();
298
299         File outFile = new File(outputFile);
300
301         if(outFile.exists()){
302             String absPath = outFile.getAbsolutePath();
303             if( ! outFile.isFile() ){
304                 String msg = MessageFormat.format(MSG_ABNFILE, absPath);
305                 errMsg(msg);
306                 exit(EXIT_IOERR);
307             }else if( ! overwrite ){
308                 String msg = MessageFormat.format(MSG_OWOUTFILE, absPath);
309                 errMsg(msg);
310                 exit(EXIT_IOERR);
311             }
312         }
313
314         try{
315             trunc(outFile);
316         }catch(IOException e){
317             ioError(e);
318         }
319
320         OutputStream os;
321         try{
322             os = new FileOutputStream(outFile);
323         }catch(FileNotFoundException e){
324             ioError(e);
325             assert false;
326             throw new AssertionError(e);
327         }
328
329         os = new BufferedOutputStream(os);
330
331         return os;
332     }
333
334     /**
335      * オプション情報に従いコンバータを生成する。
336      * @param optInfo オプション情報
337      * @return コンバータ
338      */
339     private static Vmd2XmlConv buildConverter(OptInfo optInfo){
340         Vmd2XmlConv converter = new Vmd2XmlConv();
341
342         converter.setInType (optInfo.getInFileType());
343         converter.setOutType(optInfo.getOutFileType());
344
345         converter.setNewline(optInfo.getNewline());
346         converter.setGenerator(optInfo.getGenerator());
347         converter.setQuaterniomMode(optInfo.isQuaterniomMode());
348
349         return converter;
350     }
351
352     /**
353      * 実際のコンバート作業と異常系処理を行う。
354      * <p>異常系が起きた場合、このメソッドは制御を戻さない。
355      * @param converter コンバータ
356      * @param source 入力ソース
357      * @param ostream 出力ストリーム
358      */
359     private static void doConvert(Vmd2XmlConv converter,
360                                    InputSource source,
361                                    OutputStream ostream ){
362         try{
363             converter.convert(source, ostream);
364         }catch(IOException e){
365             ioError(e);
366         }catch(IllegalVmdDataException e){
367             internalError(e);
368         }catch(MmdFormatException e){
369             vmdError(e);
370         }catch(TogaXmlException e){
371             xmlError(e);
372         }catch(SAXException e){
373             xmlError(e);
374         }
375
376         return;
377     }
378
379     /**
380      * コマンドライン文字列をオプション情報としてパースする。
381      * <p>異常系が起きた場合、このメソッドは制御を戻さない。
382      * @param args コマンドライン文字列群
383      * @return オプション情報
384      */
385     private static OptInfo parseOption(String[] args){
386         OptInfo optInfo;
387
388         try{
389             optInfo = OptInfo.parseOption(args);
390         }catch(CmdLineException e){
391             String optErrMsg = e.getLocalizedMessage();
392             errMsg(optErrMsg);
393             exit(EXIT_OPTERR);
394
395             assert false;
396             throw new AssertionError(e);
397         }
398
399         return optInfo;
400     }
401
402     /**
403      * Mainエントリ。
404      * @param args コマンドパラメータ
405      */
406     public static void main(String[] args){
407         checkJRE();
408
409         OptInfo optInfo = parseOption(args);
410         if(optInfo.needHelp()){
411             putHelp();
412             exit(EXIT_OK);
413         }
414
415         Vmd2XmlConv converter = buildConverter(optInfo);
416         InputSource source = openInfile(optInfo);
417         OutputStream ostream = openOutfile(optInfo);
418
419         doConvert(converter, source, ostream);
420
421         try{
422             ostream.close();
423         }catch(IOException e){
424             ioError(e);
425         }
426
427         exit(EXIT_OK);
428
429         return;
430     }
431
432 }