OSDN Git Service

Merge release/v1.203.2
[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.8";
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      *
114      * @param code 終了コード
115      * @see java.lang.System#exit(int)
116      */
117     private static void exit(int code){
118         System.exit(code);
119         assert false;
120         throw new AssertionError();
121     }
122
123     /**
124      * 共通エラーメッセージを出力する。
125      *
126      * @param text 個別メッセージ
127      */
128     private static void errMsg(String text){
129         String msg = MessageFormat.format(MSG_ERR, text);
130         ERROUT.println(msg);
131         return;
132     }
133
134     /**
135      * 標準エラー出力へ例外情報出力。
136      *
137      * @param ex 例外
138      * @param dumpStack スタックトレースを出力するならtrue
139      */
140     private static void thPrintln(Throwable ex, boolean dumpStack){
141         if(dumpStack){
142             ex.printStackTrace(ERROUT);
143         }else{
144             String text = ex.toString();
145             ERROUT.println(text);
146         }
147
148         return;
149     }
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      *
165      * @param ex 例外
166      */
167     private static void ioError(IOException ex){
168         thPrintln(ex);
169         exit(EXIT_IOERR);
170     }
171
172     /**
173      * XML構文エラー処理。
174      * 例外を出力してVM終了する。
175      *
176      * @param ex 例外
177      */
178     private static void xmlError(Throwable ex){
179         thPrintln(ex);
180         exit(EXIT_XMLERR);
181     }
182
183     /**
184      * PMDファイルフォーマットエラー処理。
185      * 例外を出力してVM終了する。
186      *
187      * @param ex 例外
188      */
189     private static void pmdError(MmdFormatException ex){
190         thPrintln(ex, true);
191         exit(EXIT_PMDERR);
192     }
193
194     /**
195      * 内部エラー処理。
196      * 例外を出力してVM終了する。
197      *
198      * @param ex 例外
199      */
200     private static void internalError(Throwable ex){
201         thPrintln(ex, true);
202         exit(EXIT_INTERR);
203     }
204
205     /**
206      * JREのバージョン判定を行う。
207      * 不適切ならVMごと終了。
208      */
209     private static void checkJRE(){
210         Package jrePackage = java.lang.Object.class.getPackage();
211         if( ! jrePackage.isCompatibleWith(REQUIRED_JRE)){
212             String msg = MessageFormat.format(MSG_OLDJRE, REQUIRED_JRE);
213             ERROUT.println(msg);
214             exit(EXIT_ENVERR);
215         }
216         return;
217     }
218
219     /**
220      * ヘルプメッセージを出力する。
221      */
222     private static void putHelp(){
223         String msg =
224                 MessageFormat.format(MSG_HELP,
225                 APPNAME, APPVER, APPLICENSE, APPURL);
226         ERROUT.println(msg);
227         ERROUT.println(OptSwitch.getConsoleHelp());
228         return;
229     }
230
231     /**
232      * ファイルサイズを0に切り詰める。
233      *
234      * <p>ファイルが存在しなければなにもしない。
235      *
236      * <p>通常ファイルでなければなにもしない。
237      *
238      * @param file ファイル
239      * @throws IOException 入出力エラー
240      */
241     private static void trunc(File file) throws IOException{
242         if( ! file.exists() ) return;
243         if( ! file.isFile() ) return;
244
245         if(file.length() <= 0L) return;
246
247         FileOutputStream foStream = new FileOutputStream(file);
248         try{
249             FileChannel channnel = foStream.getChannel();
250             try{
251                 channnel.truncate(0L);
252             }finally{
253                 channnel.close();
254             }
255         }finally{
256             foStream.close();
257         }
258
259         return;
260     }
261
262     /**
263      * 入力ソースを準備する。
264      *
265      * <p>入力ファイルが通常ファイルとして存在しなければエラー終了。
266      *
267      * @param optInfo オプション情報
268      * @return 入力ソース
269      */
270     private static InputSource openInfile(OptInfo optInfo){
271         String fileName = optInfo.getInFilename();
272         File inFile = new File(fileName);
273
274         if( (! inFile.exists()) || (! inFile.isFile()) ){
275             String absPath = inFile.getAbsolutePath();
276             String msg = MessageFormat.format(MSG_NOINFILE, absPath);
277             errMsg(msg);
278             exit(EXIT_IOERR);
279         }
280
281         InputSource source = XmlInputUtil.fileToSource(inFile);
282
283         return source;
284     }
285
286     /**
287      * 出力ストリームを準備する。
288      *
289      * <p>出力ファイルが通常ファイルでない場合はエラー終了。
290      *
291      * <p>既存の出力ファイルに上書き指示が伴っていなければエラー終了。
292      *
293      * @param optInfo オプション情報
294      * @return 出力ストリーム
295      */
296     private static OutputStream openOutfile(OptInfo optInfo){
297         String outputFile = optInfo.getOutFilename();
298         boolean overwrite = optInfo.overwriteMode();
299
300         File outFile = new File(outputFile);
301
302         if(outFile.exists()){
303             String absPath = outFile.getAbsolutePath();
304             if( ! outFile.isFile() ){
305                 String msg = MessageFormat.format(MSG_ABNFILE, absPath);
306                 errMsg(msg);
307                 exit(EXIT_IOERR);
308             }else if( ! overwrite ){
309                 String msg = MessageFormat.format(MSG_OWOUTFILE, absPath);
310                 errMsg(msg);
311                 exit(EXIT_IOERR);
312             }
313         }
314
315         try{
316             trunc(outFile);
317         }catch(IOException e){
318             ioError(e);
319         }
320
321         OutputStream os;
322         try{
323             os = new FileOutputStream(outFile);
324         }catch(FileNotFoundException e){
325             ioError(e);
326             assert false;
327             throw new AssertionError(e);
328         }
329
330         os = new BufferedOutputStream(os);
331
332         return os;
333     }
334
335     /**
336      * オプション情報に従いコンバータを生成する。
337      *
338      * @param optInfo オプション情報
339      * @return コンバータ
340      */
341     private static Pmd2XmlConv buildConverter(OptInfo optInfo){
342         Pmd2XmlConv converter = new Pmd2XmlConv();
343
344         converter.setInType( optInfo.getInFileType());
345         converter.setOutType(optInfo.getOutFileType());
346
347         converter.setNewline(optInfo.getNewline());
348         converter.setGenerator(optInfo.getGenerator());
349
350         return converter;
351     }
352
353     /**
354      * 実際のコンバート作業と異常系処理を行う。
355      *
356      * <p>異常系が起きた場合、このメソッドは制御を戻さない。
357      *
358      * @param converter コンバータ
359      * @param source 入力ソース
360      * @param ostream 出力ストリーム
361      */
362     private static void doConvert(Pmd2XmlConv converter,
363                                    InputSource source,
364                                    OutputStream ostream ){
365         try{
366             converter.convert(source, ostream);
367         }catch(IOException e){
368             ioError(e);
369         }catch(IllegalPmdDataException e){
370             internalError(e);
371         }catch(MmdFormatException e){
372             pmdError(e);
373         }catch(TogaXmlException e){
374             xmlError(e);
375         }catch(SAXException e){
376             xmlError(e);
377         }
378
379         return;
380     }
381
382     /**
383      * コマンドライン文字列をオプション情報としてパースする。
384      *
385      * <p>異常系が起きた場合、このメソッドは制御を戻さない。
386      *
387      * @param args コマンドライン文字列群
388      * @return オプション情報
389      */
390     private static OptInfo parseOption(String... args){
391         OptInfo optInfo;
392
393         try{
394             optInfo = OptInfo.parseOption(args);
395         }catch(CmdLineException e){
396             String optErrMsg = e.getLocalizedMessage();
397             errMsg(optErrMsg);
398             exit(EXIT_OPTERR);
399
400             assert false;
401             throw new AssertionError(e);
402         }
403
404         return optInfo;
405     }
406
407     /**
408      * Mainエントリ。
409      *
410      * @param args コマンドパラメータ
411      */
412     public static void main(String[] args){
413         checkJRE();
414
415         OptInfo optInfo = parseOption(args);
416         if(optInfo.needHelp()){
417             putHelp();
418             exit(EXIT_OK);
419         }
420
421         Pmd2XmlConv converter = buildConverter(optInfo);
422         InputSource source = openInfile(optInfo);
423         OutputStream ostream = openOutfile(optInfo);
424
425         doConvert(converter, source, ostream);
426
427         try{
428             ostream.close();
429         }catch(IOException e){
430             ioError(e);
431         }
432
433         exit(EXIT_OK);
434
435         return;
436     }
437
438 }