OSDN Git Service

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