OSDN Git Service

bbfedb6e93657f1653a743994ba2a0cba642e8ec
[jindolf/Jindolf.git] / src / main / java / jp / sfjp / jindolf / JreChecker.java
1 /*
2  * JRE checker
3  *
4  * License : The MIT License
5  * Copyright(c) 2011 olyutorskii
6  */
7
8 package jp.sfjp.jindolf;
9
10 import java.awt.Frame;
11 import java.io.PrintStream;
12 import javax.swing.JOptionPane;
13
14 /**
15  * JRE互換性のチェックを行う。
16  *
17  * <p>JREのバージョンと、
18  * JREに含まれるjava.langパッケージ仕様のバージョンは互換性があるものとする。
19  *
20  * <p>なるべく昔のJREでも動くように。
21  * 少なくとも1.2以降。できれば1.0以降。
22  * 原則、1.3以降の新設ライブラリは利用禁止。
23  *
24  * <p>なるべく昔のコンパイラでもコンパイルできるように。
25  * 少なくとも1.2以降。できれば1.0以降。
26  * 原則、assert文、総称ジェネリクス、アノテーションは禁止。
27  *
28  * <p>できればBasic-Latinに入らない文字(日本語)の出力も禁止。
29  *
30  * <p>ランタイム存在検査用のクラスは、ロードや初期化が軽いほうが望ましい。
31  * そしていずれJindolfの実行に必要なものであるのが望ましい。
32  * もしそうでない場合でも、
33  * Jindolfに関係ないクラスの連鎖ロードが起きにくいほうが望ましい。
34  */
35 public final class JreChecker {
36
37     /** Jindolfが実行時に必要とするJREの版。 */
38     public static final String REQUIRED_JRE_VER = "1.7";
39
40     /** 互換性エラーの終了コード。 */
41     public static final int EXIT_CODE_INCOMPAT_JRE = 1;
42
43     private static final PrintStream STDERR = System.err;
44
45     private static final String DIALOG_TITLE =
46             "JRE Incompatibility detected...";
47
48     private static final int MAX_LINE = 40;
49     private static final int DEF_LINES = 5;
50     private static final int INIT_SBUF = 100;
51
52
53     /**
54      * 隠しコンストラクタ。
55      *
56      * <p><code>assert false;</code> 書きたいけど書いちゃだめ。
57      */
58     private JreChecker(){
59         super();
60         return;
61     }
62
63
64     /**
65      * クラス名に相当するクラスがロードできるか判定する。
66      * @param klassName FQDNなクラス名
67      * @return ロードできたらtrue
68      */
69     public static boolean hasClass(String klassName){
70         boolean result;
71
72         try{
73             Class.forName(klassName); // 1.2Laterな3引数版メソッドは利用禁止
74             result = true;
75         }catch(ClassNotFoundException e){
76             result = false;
77         }
78
79         return result;
80     }
81
82     /**
83      * JRE 1.1 相当のランタイムライブラリが提供されているか判定する。
84      * @return 提供されているならtrue
85      * @see java.io.Serializable
86      */
87     public static boolean has11Runtime(){
88         boolean result = hasClass("java.io.Serializable");
89         return result;
90     }
91
92     /**
93      * JRE 1.2 相当のランタイムライブラリが提供されているか判定する。
94      * @return 提供されているならtrue
95      * @see java.util.Iterator
96      */
97     public static boolean has12Runtime(){
98         boolean result;
99         if(has11Runtime()) result = hasClass("java.util.Iterator");
100         else               result = false;
101         return result;
102     }
103
104     /**
105      * JRE 1.3 相当のランタイムライブラリが提供されているか判定する。
106      * @return 提供されているならtrue
107      * @see java.util.TimerTask
108      */
109     public static boolean has13Runtime(){
110         boolean result;
111         if(has12Runtime()) result = hasClass("java.util.TimerTask");
112         else               result = false;
113         return result;
114     }
115
116     /**
117      * JRE 1.4 相当のランタイムライブラリが提供されているか判定する。
118      * @return 提供されているならtrue
119      * @see java.lang.CharSequence
120      */
121     public static boolean has14Runtime(){
122         boolean result;
123         if(has13Runtime()) result = hasClass("java.lang.CharSequence");
124         else               result = false;
125         return result;
126     }
127
128     /**
129      * JRE 1.5 相当のランタイムライブラリが提供されているか判定する。
130      * @return 提供されているならtrue
131      * @see java.lang.Appendable
132      */
133     public static boolean has15Runtime(){
134         boolean result;
135         if(has14Runtime()) result = hasClass("java.lang.Appendable");
136         else               result = false;
137         return result;
138     }
139
140     /**
141      * JRE 1.6 相当のランタイムライブラリが提供されているか判定する。
142      * @return 提供されているならtrue
143      * @see java.util.Deque
144      */
145     public static boolean has16Runtime(){
146         boolean result;
147         if(has15Runtime()) result = hasClass("java.util.Deque");
148         else               result = false;
149         return result;
150     }
151
152     /**
153      * JRE 1.7 相当のランタイムライブラリが提供されているか判定する。
154      * @return 提供されているならtrue
155      * @see java.lang.AutoCloseable
156      */
157     public static boolean has17Runtime(){
158         boolean result;
159         if(has16Runtime()) result = hasClass("java.lang.AutoCloseable");
160         else               result = false;
161         return result;
162     }
163
164     // TODO JRE1.8 対応
165
166     /**
167      * JREもしくは<code>java.lang</code>パッケージの
168      * 仕様バージョンを返す。
169      * <ol>
170      * <li>システムプロパティ<code>java.specification.version</code>
171      * <li>システムプロパティ<code>java.version</code>
172      * <li><code>java.lang</code>パッケージの仕様バージョン
173      * </ol>の順でバージョンが求められる。
174      * @return 仕様バージョン文字列。不明ならnull
175      */
176     public static String getLangPkgSpec(){
177         String result;
178
179         try{
180             result = System.getProperty("java.specification.version");
181         }catch(SecurityException e){
182             result = null;
183         }
184         if(result != null) return result;
185
186         try{
187             result = System.getProperty("java.version");
188         }catch(SecurityException e){
189             result = null;
190         }
191         if(result != null) return result;
192
193         Package javaLangPkg = java.lang.Object.class.getPackage();
194         if(javaLangPkg == null) return null;
195
196         result = javaLangPkg.getSpecificationVersion();
197         return result;
198     }
199
200     /**
201      * JREのインストール情報を返す。
202      * システムプロパティ<code>java.home</code>の取得が試みられる。
203      * @return インストール情報。不明ならnull
204      */
205     public static String getJreHome(){
206         String result;
207
208         try{
209             result = System.getProperty("java.home");
210         }catch(SecurityException e){
211             result = null;
212         }
213
214         return result;
215     }
216
217     /**
218      * 非互換エラーメッセージを組み立てる。
219      * @return エラーメッセージ
220      */
221     public static String buildErrMessage(){
222         // このクラスではStringBuilder禁止
223         StringBuffer message = new StringBuffer(INIT_SBUF);
224
225         message.append("ERROR : Java JRE ")
226                .append(REQUIRED_JRE_VER)
227                .append(" compatible or later required.");
228
229         String specVer = getLangPkgSpec();
230         if(specVer != null){
231             message.append("\nbut\u0020")
232                    .append(specVer)
233                    .append("\u0020detected.");
234         }
235
236         String jreHome = getJreHome();
237         if(jreHome != null){
238             message.append("  [ ")
239                    .append(jreHome)
240                    .append(" ]");
241         }
242
243         return message.toString();
244     }
245
246     /**
247      * 指定された文字数で行の長さを改行文字で揃える。
248      *
249      * <p>サロゲートペアは無視される。
250      *
251      * @param text 文字列
252      * @param limit 行ごとの最大文字数
253      * @return 改行済みの文字列
254      */
255     public static String alignLine(String text, int limit){
256         // このクラスではStringBuilder禁止
257         int textLength = text.length();
258         StringBuffer message = new StringBuffer(textLength + DEF_LINES);
259
260         int lineLength = 0;
261
262         for(int idx = 0; idx < textLength; idx++){
263             if(lineLength >= limit){
264                 message.append('\n');
265                 lineLength = 0;
266             }
267
268             char ch = text.charAt(idx);
269             message.append(ch);
270
271             if(ch == '\n') lineLength = 0;
272             else           lineLength++;
273         }
274
275         String result = message.toString();
276         return result;
277     }
278
279     /**
280      * JRE環境をチェックする。(JRE1.7)
281      *
282      * <p>もしJREの非互換性が検出されたらエラーメッセージを報告する。
283      *
284      * @return 互換性があれば0、無ければ非0
285      */
286     public static int checkJre(){
287         if(has17Runtime()) return 0;
288
289         String message = buildErrMessage();
290         STDERR.println(message);
291         STDERR.flush();
292         if(has12Runtime()){
293             showErrorDialog(message);
294         }
295
296         return EXIT_CODE_INCOMPAT_JRE;
297     }
298
299     /**
300      * Swingダイアログでエラーを報告する。
301      *
302      * <p>ボタンを押すまでの間、実行はブロックされる。
303      *
304      * <p>GUIに接続できなければ何か例外を投げるかもしれない。
305      *
306      * @param text エラー文面
307      */
308     public static void showErrorDialog(String text){
309         String aligned = alignLine(text, MAX_LINE);
310
311         Frame parent = null;
312         JOptionPane.showMessageDialog(
313                 parent,
314                 aligned, DIALOG_TITLE,
315                 JOptionPane.ERROR_MESSAGE );
316
317         return;
318     }
319
320 }