OSDN Git Service

XML処理の整理
[jindolf/JinArchiver.git] / src / main / java / jp / sourceforge / jindolf / archiver / JinArchiver.java
1 /*
2  * main entry
3  *
4  * License : The MIT License
5  * Copyright(c) 2008 olyutorskii
6  */
7
8 package jp.sourceforge.jindolf.archiver;
9
10 import java.io.BufferedOutputStream;
11 import java.io.BufferedWriter;
12 import java.io.File;
13 import java.io.FileOutputStream;
14 import java.io.IOException;
15 import java.io.InputStream;
16 import java.io.OutputStream;
17 import java.io.OutputStreamWriter;
18 import java.io.Writer;
19 import java.text.MessageFormat;
20 import java.util.List;
21 import java.util.Properties;
22 import jp.sourceforge.jindolf.corelib.LandDef;
23 import jp.sourceforge.jindolf.parser.DecodeException;
24 import jp.sourceforge.jindolf.parser.HtmlParseException;
25
26 /**
27  * メインエントリ。
28  */
29 public final class JinArchiver{
30
31     /** Generator. */
32     public static final String GENERATOR;
33
34     /** このClass。 */
35     private static final Class<?> SELF_KLASS;
36     /** このPackage。 */
37     private static final Package  SELF_PACKAGE;
38     /** タイトル。 */
39     private static final String TITLE;
40     /** バージョン。 */
41     private static final String VERSION;
42
43     /** バージョン定義リソース。 */
44     private static final String RES_VERDEF = "resources/version.properties";
45
46
47     static{
48         SELF_KLASS   = JinArchiver.class;
49         SELF_PACKAGE = SELF_KLASS.getPackage();
50
51         Properties verProp = loadVersionDefinition(SELF_KLASS);
52         TITLE   = getPackageInfo(verProp, "pkg-title.",   "Unknown");
53         VERSION = getPackageInfo(verProp, "pkg-version.", "0");
54         GENERATOR = TITLE + " " + VERSION;
55     }
56
57
58     /**
59      * 隠しコンストラクタ。
60      */
61     private JinArchiver(){
62         throw new Error();
63     }
64
65
66     /**
67      * リソース上のパッケージ定義プロパティをロードする。
68      * MANIFEST.MFが参照できない実行環境での代替品。
69      * @param klass パッケージを構成する任意のクラス
70      * @return プロパティ
71      */
72     private static Properties loadVersionDefinition(Class<?> klass){
73         Properties result = new Properties();
74
75         InputStream istream = klass.getResourceAsStream(RES_VERDEF);
76         try{
77             result.load(istream);
78         }catch(IOException e){
79             return result;
80         }finally{
81             try{
82                 istream.close();
83             }catch(IOException e){
84                 return result;
85             }
86         }
87
88         return result;
89     }
90
91     /**
92      * リソース上のプロパティから
93      * このクラスのパッケージのパッケージ情報を取得する。
94      * MANIFEST.MFが参照できない実行環境での代替品。
95      * @param prop プロパティ
96      * @param prefix 接頭辞
97      * @param defValue 見つからなかった場合のデフォルト値
98      * @return パッケージ情報
99      */
100     private static String getPackageInfo(Properties prop,
101                                           String prefix,
102                                           String defValue){
103         return getPackageInfo(prop, SELF_PACKAGE, prefix, defValue);
104     }
105
106     /**
107      * リソース上のプロパティからパッケージ情報を取得する。
108      * MANIFEST.MFが参照できない実行環境での代替品。
109      * @param prop プロパティ
110      * @param pkg 任意のパッケージ
111      * @param prefix 接頭辞
112      * @param defValue デフォルト値
113      * @return 見つからなかった場合のパッケージ情報
114      */
115     private static String getPackageInfo(Properties prop,
116                                           Package pkg,
117                                           String prefix,
118                                           String defValue){
119         String propName = prefix + pkg.getName();
120         String result = prop.getProperty(propName, defValue);
121         return result;
122     }
123
124     /**
125      * System.err.println()のWrapper。
126      * @param text 出力テキスト
127      */
128     private static void errprintln(CharSequence text){
129         System.err.println(text);
130         return;
131     }
132
133     /**
134      * プログラムの終了。
135      * @param code プロセスコード。
136      */
137     private static void exit(int code){
138         System.exit(code);
139         assert false;
140         return;
141     }
142
143     /**
144      * ヘルプメッセージ出力。
145      */
146     private static void helpMessage(){
147         errprintln(
148                 "\n" + GENERATOR + " 人狼BBS アーカイブ作成ツール\n\n"
149                 +"-h, -help, -?\n\tヘルプメッセージ\n"
150                 +"-land 国識別子\n"
151                 +"-vid 村番号\n"
152                 +"-outdir 出力ディレクトリ\n"
153                 +"-stdout\n\t標準出力へ出力\n\n"
154                 +"※ -outdir と -stdout は排他指定\n"
155                 );
156
157         String landList = LandUtils.getLandList();
158         errprintln("利用可能な国識別子は " + landList + "\n");
159
160         return;
161     }
162
163     /**
164      * オプション文字列を解析する。
165      * @param args オプション文字列
166      */
167     private static void parseOption(String[] args){
168         if(args.length <= 0){
169             helpMessage();
170             exit(0);
171             return;
172         }
173
174         LandDef landDef = null;
175         int vid = -1;
176         String outdir = null;
177         boolean stdout = false;
178
179         for(int pos = 0; pos < args.length; pos++){
180             String arg = args[pos];
181
182             if( ! arg.startsWith("-") ){
183                 errprintln("不正なオプションです。 " + arg);
184                 exit(1);
185                 return;
186             }
187
188             if(arg.equals("-h") || arg.equals("-help") || arg.equals("-?")){
189                 helpMessage();
190                 exit(0);
191                 return;
192             }
193
194             if(arg.equals("-stdout")){
195                 stdout = true;
196                 outdir = null;
197                 continue;
198             }
199
200             if(++pos >= args.length){
201                 errprintln(
202                         "オプション " + arg + " に引数がありません。");
203                 exit(1);
204                 return;
205             }
206
207             String val = args[pos];
208             if(arg.equals("-land")){
209                 landDef = LandUtils.getLandDef(val);
210                 if(landDef == null){
211                     errprintln("不正な国識別子です。 " + val);
212                     exit(1);
213                     return;
214                 }
215             }else if(arg.equals("-vid")){
216                 vid = Integer.parseInt(val);
217                 if(vid < 0){
218                     errprintln("不正な村番号です。 " + vid);
219                     exit(1);
220                     return;
221                 }
222             }else if(arg.equals("-outdir")){
223                 outdir = val;
224                 stdout = false;
225             }else{
226                 errprintln("不正なオプションです。 " + arg);
227                 exit(1);
228                 return;
229             }
230         }
231
232         if(landDef == null){
233             errprintln(
234                     "-land オプションで国識別子を指定してください。");
235             exit(1);
236             return;
237         }
238
239         if(vid < 0){
240             errprintln(
241                     "-vid オプションで村番号を指定してください。");
242             exit(1);
243             return;
244         }
245
246         if(   (outdir == null && stdout == false)
247            || (outdir != null && stdout == true)  ){
248             errprintln(
249                     "-outdir か -stdout のどちらか一方を指定してください。");
250             exit(1);
251             return;
252         }
253
254         Writer writer;
255         if(outdir != null){
256             writer = getFileWriter(outdir, landDef, vid);
257         }else{
258             writer = getStdOutWriter();
259         }
260
261         writer = ValidateTask.wrapValidator(writer);
262
263         try{
264             dump(writer, landDef, vid);
265         }catch(IOException e){
266             abortWithException(e);
267         }catch(DecodeException e){
268             abortWithException(e);
269         }catch(HtmlParseException e){
270             abortWithException(e);
271         }
272
273         return;
274     }
275
276     /**
277      * 例外によるアプリ終了。
278      * @param e 例外
279      */
280     private static void abortWithException(Exception e){
281         e.printStackTrace(System.err);
282         errprintln("処理を続行できません。");
283         exit(1);
284         return;
285     }
286
287     /**
288      * 主処理。人狼サーバからXHTMLを読み込み。XMLで出力。
289      * @param writer 出力先
290      * @param landDef 国情報
291      * @param vid 村番号
292      * @throws IOException 入出力エラー
293      * @throws DecodeException デコードエラー
294      * @throws HtmlParseException パースエラー
295      */
296     public static void dump(Writer writer, LandDef landDef, int vid)
297             throws IOException, DecodeException, HtmlParseException{
298         List<PeriodResource> resourceList =
299                 HttpAccess.loadResourceList(landDef, vid);
300         VillageData village = new VillageData(resourceList);
301
302         Builder.fillVillageData(village);
303         XmlUtils.dumpVillageData(writer, village);
304
305         return;
306     }
307
308     /**
309      * 標準出力への出力先を得る。
310      * @return 出力先
311      */
312     public static Writer getStdOutWriter(){
313         OutputStream ostream;
314         ostream = new BufferedOutputStream(System.out);
315         Writer writer;
316         try{
317             writer = new OutputStreamWriter(ostream, "UTF-8");
318             writer = new BufferedWriter(writer, 4 * 1024);
319         }catch(IOException e){
320             errprintln(
321                     "標準出力に書き込めません。");
322             exit(1);
323             return null;
324         }
325         return writer;
326     }
327
328     /**
329      * ローカルファイルへの出力先を得る。
330      * @param outdir 出力ディレクトリ
331      * @param landDef 国情報
332      * @param vid 村番号
333      * @return 出力先
334      */
335     public static Writer getFileWriter(String outdir,
336                                          LandDef landDef,
337                                          int vid ){
338             File outFile = new File(outdir);
339             if( ! outFile.exists() ){
340                 errprintln(
341                         outdir + " が存在しません。");
342                 exit(1);
343                 return null;
344             }
345             if( ! outFile.isDirectory() ){
346                 errprintln(
347                         outdir + " はディレクトリではありません。");
348                 exit(1);
349                 return null;
350             }
351             if( ! outFile.canWrite() ){
352                 errprintln(
353                         outdir + " に書き込めません。");
354                 exit(1);
355                 return null;
356             }
357             String fname = MessageFormat.format(
358                 "jin_{0}_{1,number,#00000}.xml", landDef.getLandId(), vid);
359             File xmlFile = new File(outFile, fname);
360             boolean created;
361             try{
362                 created = xmlFile.createNewFile();
363             }catch(IOException e){
364                 errprintln(
365                         xmlFile.getName() + " が作成できません。");
366                 exit(1);
367                 return null;
368             }
369             if( ! created ){
370                 errprintln(
371                         fname + " が既に" + outdir + "に存在します。");
372                 exit(1);
373                 return null;
374             }
375             /* JRE 1.6 only
376             xmlFile.setReadable(true);
377             xmlFile.setWritable(true);
378             xmlFile.setExecutable(false, false);
379             */
380             Writer writer;
381             try{
382                 OutputStream ostream;
383                 ostream = new FileOutputStream(xmlFile);
384                 ostream = new BufferedOutputStream(ostream, 4 * 1024);
385                 writer = new OutputStreamWriter(ostream, "UTF-8");
386                 writer = new BufferedWriter(writer, 4 * 1024);
387             }catch(IOException e){
388                 errprintln(
389                         xmlFile.getName() + " に書き込めません。");
390                 exit(1);
391                 return null;
392             }
393
394             return writer;
395     }
396
397     /**
398      * スタートアップエントリ。
399      * @param args 引数
400      */
401     public static void main(String[] args){
402         parseOption(args);
403         exit(0);
404         return;
405     }
406
407 }