OSDN Git Service

checkstyle警告対応
[jindolf/Jindolf.git] / src / main / java / jp / sfjp / jindolf / JindolfMain.java
1 /*
2  * Jindolf main class
3  *
4  * License : The MIT License
5  * Copyright(c) 2008 olyutorskii
6  */
7
8 package jp.sfjp.jindolf;
9
10 import java.awt.Dimension;
11 import java.awt.EventQueue;
12 import java.awt.Window;
13 import java.io.PrintStream;
14 import java.lang.reflect.InvocationTargetException;
15 import java.text.MessageFormat;
16 import java.util.logging.Level;
17 import java.util.logging.Logger;
18 import javax.swing.ImageIcon;
19 import javax.swing.JFrame;
20 import javax.swing.JLabel;
21 import javax.swing.JWindow;
22 import javax.swing.UIManager;
23 import jp.sfjp.jindolf.config.AppSetting;
24 import jp.sfjp.jindolf.config.CmdOption;
25 import jp.sfjp.jindolf.config.ConfigStore;
26 import jp.sfjp.jindolf.config.EnvInfo;
27 import jp.sfjp.jindolf.config.OptionInfo;
28 import jp.sfjp.jindolf.data.LandsModel;
29 import jp.sfjp.jindolf.log.LogUtils;
30 import jp.sfjp.jindolf.log.LoggingDispatcher;
31 import jp.sfjp.jindolf.util.GUIUtils;
32 import jp.sfjp.jindolf.view.ActionManager;
33 import jp.sfjp.jindolf.view.WindowManager;
34
35 /**
36  * Jindolf スタートアップクラス。
37  *
38  * <p>{@link JindolfJre17}の下請けとして本格的なJindolf起動処理に入る。
39  */
40 public final class JindolfMain {
41
42     /** クラスロード時のナノカウント。 */
43     public static final long NANOCT_LOADED;
44     /** クラスロード時刻(エポックmsec)。 */
45     public static final long EPOCHMS_LOADED;
46
47
48     /** ロガー。 */
49     private static final Logger LOGGER = Logger.getAnonymousLogger();
50
51     /** スプラッシュロゴ。 */
52     private static final String RES_LOGOICON =
53             "resources/image/logo.png";
54
55     private static final String LOG_LOADED =
56               "{0} は {1,date} {2,time} に"
57             + "VM上のクラス {3} としてロードされました。 ";
58     private static final String LOG_NANOCT =
59             "Initial Nano-Count : {0}";
60     private static final String LOG_HEAP =
61             "Max-heap : {0} Bytes   Total-heap : {1} Bytes";
62     private static final String LOG_CONF =
63             "設定格納ディレクトリに[ {0} ]が指定されました。";
64     private static final String LOG_NOCONF =
65             "設定格納ディレクトリは使いません。";
66     private static final String WARNMSG_SPLASH =
67               "JRE1.6以降では、Jindolfの-nosplashオプションは無効です。"
68             + "Java実行系の方でスプラッシュ画面の非表示を"
69             + "指示してください(おそらく空の-splash:オプション)";
70     private static final String FATALMSG_INITFAIL =
71             "アプリケーション初期化に失敗しました";
72     private static final String ERRMSG_HELP =
73               "起動オプション一覧は、"
74             + "起動オプションに「{0}」を指定すると確認できます。";
75
76     private static final PrintStream STDOUT = System.out;
77     private static final PrintStream STDERR = System.err;
78
79     static{
80         NANOCT_LOADED  = System.nanoTime();
81         EPOCHMS_LOADED = System.currentTimeMillis();
82     }
83
84
85     /**
86      * 隠れコンストラクタ。
87      */
88     private JindolfMain(){
89         assert false;
90     }
91
92
93     /**
94      * 標準出力および標準エラー出力をフラッシュする。
95      */
96     private static void flush(){
97         STDOUT.flush();
98         STDERR.flush();
99         return;
100     }
101
102     /**
103      * 標準出力端末にヘルプメッセージ(オプションの説明)を表示する。
104      */
105     private static void showHelpMessage(){
106         flush();
107
108         String helpText = CmdOption.getHelpText();
109         STDOUT.print(helpText);
110
111         flush();
112
113         return;
114     }
115
116     /**
117      * スプラッシュウィンドウを表示する。
118      *
119      * <p>JRE1.6以降では何も表示しない。
120      *
121      * @return スプラッシュウィンドウ。JRE1.6以降ならnullを返す。
122      */
123     @SuppressWarnings("CallToThreadYield")
124     private static Window showSplash(){
125         if(JreChecker.has16Runtime()) return null;
126
127         Window splashWindow = new JWindow();
128
129         ImageIcon logo = ResourceManager.getImageIcon(RES_LOGOICON);
130         JLabel splashLabel = new JLabel(logo);
131         splashWindow.add(splashLabel);
132
133         splashWindow.pack();
134         splashWindow.setLocationRelativeTo(null); // locate center
135         splashWindow.setVisible(true);
136
137         Thread.yield();
138
139         return splashWindow;
140     }
141
142     /**
143      * スプラッシュウィンドウを隠す。
144      * @param splashWindow スプラッシュウィンドウ。nullならなにもしない。
145      */
146     private static void hideSplash(final Window splashWindow){
147         if(splashWindow == null) return;
148
149         EventQueue.invokeLater(new Runnable(){
150             /** {@inheritDoc} */
151             @Override
152             public void run(){
153                 splashWindow.setVisible(false);
154                 splashWindow.dispose();
155                 return;
156             }
157         });
158
159         return;
160     }
161
162     /**
163      * 起動時の諸々の情報をログ出力する。
164      * @param appSetting  アプリ設定
165      */
166     private static void dumpBootInfo(AppSetting appSetting){
167         Object[] logArgs;
168
169         logArgs = new Object[]{
170             VerInfo.ID,
171             EPOCHMS_LOADED,
172             EPOCHMS_LOADED,
173             Jindolf.class.getName(),
174         };
175         LOGGER.log(Level.INFO, LOG_LOADED, logArgs);
176
177         LOGGER.log(Level.INFO, LOG_NANOCT, NANOCT_LOADED);
178
179         Runtime runtime = Runtime.getRuntime();
180         logArgs = new Object[]{
181             runtime.maxMemory(),
182             runtime.totalMemory(),
183         };
184         LOGGER.log(Level.INFO, LOG_HEAP, logArgs);
185
186         OptionInfo optinfo = appSetting.getOptionInfo();
187         StringBuilder bootArgs = new StringBuilder();
188         bootArgs.append("\n\n").append("起動時引数:\n");
189         for(String arg : optinfo.getInvokeArgList()){
190             bootArgs.append("\u0020\u0020").append(arg).append('\n');
191         }
192         bootArgs.append('\n');
193         bootArgs.append(EnvInfo.getVMInfo());
194         LOGGER.info(bootArgs.toString());
195
196         ConfigStore configStore = appSetting.getConfigStore();
197         if(configStore.useStoreFile()){
198             LOGGER.log(Level.INFO, LOG_CONF, configStore.getConfigPath());
199         }else{
200             LOGGER.info(LOG_NOCONF);
201         }
202
203         if(   JreChecker.has16Runtime()
204            && optinfo.hasOption(CmdOption.OPT_NOSPLASH) ){
205             LOGGER.warning(WARNMSG_SPLASH);
206         }
207
208         return;
209     }
210
211     /**
212      * JindolfMain のスタートアップエントリ。
213      * @param args コマンドライン引数
214      * @return 起動に成功すれば0。失敗したら0以外。
215      */
216     public static int main(String... args){
217         OptionInfo optinfo;
218         int exitCode;
219
220         try{
221             optinfo = OptionInfo.parseOptions(args);
222         }catch(IllegalArgumentException e){
223             String message = e.getLocalizedMessage();
224             STDERR.println(message);
225
226             String info =
227                     MessageFormat.format(ERRMSG_HELP, CmdOption.OPT_HELP);
228             STDERR.println(info);
229
230             exitCode = 1;
231             return exitCode;
232         }
233
234         exitCode = main(optinfo);
235
236         return exitCode;
237     }
238
239     /**
240      * JindolfMain のスタートアップエントリ。
241      * @param optinfo コマンドライン引数情報
242      * @return 起動に成功すれば0。失敗したら0以外。
243      */
244     public static int main(OptionInfo optinfo){
245         int exitCode;
246
247         if(optinfo.hasOption(CmdOption.OPT_HELP)){
248             showHelpMessage();
249             exitCode = 0;
250             return exitCode;
251         }
252
253         if(optinfo.hasOption(CmdOption.OPT_VERSION)){
254             STDOUT.println(VerInfo.ID);
255             exitCode = 0;
256             return exitCode;
257         }
258
259         // ここ以降、アプリウィンドウの生成と表示に向けてまっしぐら。
260
261         // あらゆるSwing文字列表示処理より前に必要。
262         // システムプロパティ swing.boldMetal は無視される。
263         Boolean boldFlag;
264         if(optinfo.hasOption(CmdOption.OPT_BOLDMETAL)){
265             // もの凄く日本語表示が汚くなるかもよ!注意
266             boldFlag = Boolean.TRUE;
267         }else{
268             boldFlag = Boolean.FALSE;
269         }
270         UIManager.put("swing.boldMetal", boldFlag);
271
272         // JRE1.5用スプラッシュウィンドウ。だけどもういらん。
273         Window splashWindow = null;
274         if( ! optinfo.hasOption(CmdOption.OPT_NOSPLASH) ){
275             splashWindow = showSplash();
276         }
277
278         try{
279             exitCode = splashedMain(optinfo);
280         }finally{
281             hideSplash(splashWindow);
282         }
283
284         return exitCode;
285     }
286
287     /**
288      * JindolfMain のスタートアップエントリ。
289      *
290      * <p>スプラッシュウィンドウが出ている状態。
291      * 時間のかかる初期化処理はなるべくこの中へ。
292      *
293      * @param optinfo コマンドライン引数情報
294      * @return 起動に成功すれば0。失敗したら0以外。
295      */
296     public static int splashedMain(OptionInfo optinfo){
297         if(optinfo.hasOption(CmdOption.OPT_VMINFO)){
298             STDOUT.println(EnvInfo.getVMInfo());
299         }
300
301         LogUtils.initRootLogger(optinfo.hasOption(CmdOption.OPT_CONSOLELOG));
302         // ここからロギング解禁
303
304         final AppSetting appSetting = new AppSetting(optinfo);
305         dumpBootInfo(appSetting);
306
307         ConfigStore configStore = appSetting.getConfigStore();
308         configStore.prepareConfigDir();
309         configStore.tryLock();
310         // ここから設定格納ディレクトリ解禁
311
312         appSetting.loadConfig();
313
314         final Runtime runtime = Runtime.getRuntime();
315         runtime.addShutdownHook(new Thread(){
316             /** {@inheritDoc} */
317             @Override
318             @SuppressWarnings("CallToThreadYield")
319             public void run(){
320                 LOGGER.info("シャットダウン処理に入ります…");
321                 flush();
322                 runtime.gc();
323                 Thread.yield();
324                 runtime.runFinalization(); // 危険?
325                 Thread.yield();
326                 return;
327             }
328         });
329
330         LoggingDispatcher.replaceEventQueue();
331
332         int exitCode = 0;
333         try{
334             EventQueue.invokeAndWait(new Runnable(){
335                 /** {@inheritDoc} */
336                 @Override
337                 public void run(){
338                     startGUI(appSetting);
339                     return;
340                 }
341             });
342         }catch(InvocationTargetException e){
343             LOGGER.log(Level.SEVERE, FATALMSG_INITFAIL, e);
344             e.printStackTrace(STDERR);
345             exitCode = 1;
346         }catch(InterruptedException e){
347             LOGGER.log(Level.SEVERE, FATALMSG_INITFAIL, e);
348             e.printStackTrace(STDERR);
349             exitCode = 1;
350         }
351
352         return exitCode;
353     }
354
355     /**
356      * AWTイベントディスパッチスレッド版スタートアップエントリ。
357      * @param appSetting アプリ設定
358      */
359     private static void startGUI(AppSetting appSetting){
360         JFrame topFrame = buildMVC(appSetting);
361
362         GUIUtils.modifyWindowAttributes(topFrame, true, false, true);
363
364         topFrame.pack();
365
366         Dimension initGeometry =
367                 new Dimension(appSetting.initialFrameWidth(),
368                               appSetting.initialFrameHeight());
369         topFrame.setSize(initGeometry);
370
371         if(   appSetting.initialFrameXpos() <= Integer.MIN_VALUE
372            || appSetting.initialFrameYpos() <= Integer.MIN_VALUE ){
373             topFrame.setLocationByPlatform(true);
374         }else{
375             topFrame.setLocation(appSetting.initialFrameXpos(),
376                                  appSetting.initialFrameYpos() );
377         }
378
379         topFrame.setVisible(true);
380
381         return;
382     }
383
384     /**
385      * モデル・ビュー・コントローラの結合。
386      * @param appSetting アプリ設定
387      * @return アプリケーションのトップフレーム
388      */
389     private static JFrame buildMVC(AppSetting appSetting){
390         LandsModel model = new LandsModel();
391         WindowManager windowManager = new WindowManager();
392         ActionManager actionManager = new ActionManager();
393
394         model.loadLandList();
395
396         Controller controller = new Controller(model,
397                                                windowManager,
398                                                actionManager,
399                                                appSetting );
400
401         JFrame topFrame = controller.getTopFrame();
402
403         return topFrame;
404     }
405
406 }