OSDN Git Service

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