OSDN Git Service

from subversion repository
[jindolf/Jindolf.git] / src / main / java / jp / sourceforge / jindolf / FileUtils.java
1 /*\r
2  * file utilities\r
3  *\r
4  * Copyright(c) 2009 olyutorskii\r
5  * $Id: FileUtils.java 952 2009-12-06 14:29:10Z olyutorskii $\r
6  */\r
7 \r
8 package jp.sourceforge.jindolf;\r
9 \r
10 import java.io.File;\r
11 import java.lang.reflect.InvocationTargetException;\r
12 import java.lang.reflect.Method;\r
13 import java.lang.reflect.Modifier;\r
14 import java.net.URI;\r
15 import java.net.URISyntaxException;\r
16 import java.net.URL;\r
17 import java.security.CodeSource;\r
18 import java.security.ProtectionDomain;\r
19 import java.util.Locale;\r
20 \r
21 /**\r
22  * 諸々のファイル操作ユーティリティ。\r
23  * JRE1.6 API へのリフレクションアクセスを含む。\r
24  */\r
25 public final class FileUtils{\r
26 \r
27     private static final String SCHEME_FILE = "file";\r
28 \r
29     /** JRE1.6のjava.io.File#setReadableに相当。 */\r
30     private static final Method METHOD_SETREADABLE;\r
31     /** JRE1.6のjava.io.File#setWritableに相当。 */\r
32     private static final Method METHOD_SETWRITABLE;\r
33     /** Locale.ROOT代替品。 */\r
34     private static final Locale ROOT = new Locale("", "", "");\r
35 \r
36     static{\r
37         Method method;\r
38         int modifiers;\r
39 \r
40         try{\r
41             method = File.class.getMethod(\r
42                     "setReadable", Boolean.TYPE, Boolean.TYPE);\r
43             modifiers = method.getModifiers();\r
44             if( ! Modifier.isPublic(modifiers) ){\r
45                 method = null;\r
46             }\r
47         }catch(NoSuchMethodException e){\r
48             method = null;\r
49         }catch(SecurityException e){\r
50             method = null;\r
51         }\r
52         METHOD_SETREADABLE = method;\r
53 \r
54         try{\r
55             method = File.class.getMethod(\r
56                     "setWritable", Boolean.TYPE, Boolean.TYPE);\r
57             modifiers = method.getModifiers();\r
58             if( ! Modifier.isPublic(modifiers) ){\r
59                 method = null;\r
60             }\r
61         }catch(NoSuchMethodException e){\r
62             method = null;\r
63         }catch(SecurityException e){\r
64             method = null;\r
65         }\r
66         METHOD_SETWRITABLE = method;\r
67 \r
68         assert ! ( isMacOSXFs() && isWindowsOSFs() );\r
69     }\r
70 \r
71     /**\r
72      * なるべく自分にだけ許可を与え自分以外には許可を与えないように\r
73      * ファイル属性を操作する。\r
74      * @param method setReadableかsetWritableのいずれかのメソッド。\r
75      * nullならなにもしない。\r
76      * @param file 操作対象のファイル。\r
77      * @return 成功すればtrue\r
78      * @throws SecurityException セキュリティ上の許可が無い場合\r
79      */\r
80     private static boolean invokeOwnerOnly(Method method, File file)\r
81             throws SecurityException{\r
82         if(method == null) return false;\r
83         if(file == null) throw new NullPointerException();\r
84 \r
85         Object result1;\r
86         Object result2;\r
87         try{\r
88             result1 = method.invoke(file, false, false);\r
89             result2 = method.invoke(file, true,  true);\r
90         }catch(IllegalAccessException e){\r
91             assert false;\r
92             return false;\r
93         }catch(IllegalArgumentException e){\r
94             assert false;\r
95             return false;\r
96         }catch(ExceptionInInitializerError e){\r
97             assert false;\r
98             return false;\r
99         }catch(InvocationTargetException e){\r
100             Throwable cause = e.getCause();\r
101             if(cause instanceof SecurityException){\r
102                 throw (SecurityException) cause;\r
103             }else if(cause instanceof RuntimeException){\r
104                 throw (RuntimeException) cause;\r
105             }else if(cause instanceof Error){\r
106                 throw (Error) cause;\r
107             }else{\r
108                 assert false;\r
109             }\r
110             return false;\r
111         }\r
112 \r
113         assert result1 instanceof Boolean;\r
114         assert result2 instanceof Boolean;\r
115         Boolean bresult1 = (Boolean) result1;\r
116         Boolean bresult2 = (Boolean) result2;\r
117 \r
118         return bresult1 && bresult2;\r
119     }\r
120 \r
121     /**\r
122      * なるべく自分にだけ読み書き許可を与え\r
123      * 自分以外には読み書き許可を与えないように\r
124      * ファイル属性を操作する。\r
125      * JRE1.6環境でなければなにもしない。\r
126      * @param file 操作対象ファイル\r
127      * @return 成功すればtrue\r
128      * @throws SecurityException セキュリティ上の許可が無い場合\r
129      */\r
130     public static boolean setOwnerOnlyAccess(File file)\r
131             throws SecurityException{\r
132         boolean readresult  = invokeOwnerOnly(METHOD_SETREADABLE, file);\r
133         boolean writeresult = invokeOwnerOnly(METHOD_SETWRITABLE, file);\r
134         return readresult & writeresult;\r
135     }\r
136 \r
137     /**\r
138      * 任意の絶対パスの祖先の内、存在するもっとも近い祖先を返す。\r
139      * @param file 任意の絶対パス\r
140      * @return 存在するもっとも近い祖先。一つも存在しなければnull。\r
141      * @throws IllegalArgumentException 引数が絶対パスでない\r
142      */\r
143     public static File findExistsAncestor(File file)\r
144             throws IllegalArgumentException{\r
145         if(file == null) return null;\r
146         if( ! file.isAbsolute() ) throw new IllegalArgumentException();\r
147         if(file.exists()) return file;\r
148         File parent = file.getParentFile();\r
149         return findExistsAncestor(parent);\r
150     }\r
151 \r
152     /**\r
153      * 任意の絶対パスのルートファイルシステムもしくはドライブレターを返す。\r
154      * @param file 任意の絶対パス\r
155      * @return ルートファイルシステムもしくはドライブレター\r
156      * @throws IllegalArgumentException 引数が絶対パスでない\r
157      */\r
158     public static File findRootFile(File file)\r
159             throws IllegalArgumentException{\r
160         if( ! file.isAbsolute() ) throw new IllegalArgumentException();\r
161         File parent = file.getParentFile();\r
162         if(parent == null) return file;\r
163         return findRootFile(parent);\r
164     }\r
165 \r
166     /**\r
167      * 相対パスの絶対パス化を試みる。\r
168      * @param file 対象パス\r
169      * @return 絶対パス。絶対化に失敗した場合は元の引数。\r
170      */\r
171     public static File supplyFullPath(File file){\r
172         if(file.isAbsolute()) return file;\r
173 \r
174         File absFile;\r
175 \r
176         try{\r
177             absFile = file.getAbsoluteFile();\r
178         }catch(SecurityException e){\r
179             return file;\r
180         }\r
181 \r
182         return absFile;\r
183     }\r
184 \r
185     /**\r
186      * 任意のディレクトリがアクセス可能な状態にあるか判定する。\r
187      * アクセス可能の条件を満たすためには、与えられたパスが\r
188      * 存在し、\r
189      * かつディレクトリであり、\r
190      * かつ読み込み可能であり、\r
191      * かつ書き込み可能\r
192      * でなければならない。\r
193      * @param path 任意のディレクトリ\r
194      * @return アクセス可能ならtrue\r
195      */\r
196     public static boolean isAccessibleDirectory(File path){\r
197         if(path == null) return false;\r
198 \r
199         if( ! path.exists() )      return false;\r
200         if( ! path.isDirectory() ) return false;\r
201         if( ! path.canRead() )     return false;\r
202         if( ! path.canWrite() )    return false;\r
203 \r
204         return true;\r
205     }\r
206 \r
207     /**\r
208      * クラスがローカルファイルからロードされたのであれば\r
209      * そのファイルを返す。\r
210      * @param klass 任意のクラス\r
211      * @return ロード元ファイル。見つからなければnull。\r
212      */\r
213     public static File getClassSourceFile(Class<?> klass){\r
214         ProtectionDomain domain;\r
215         try{\r
216             domain = klass.getProtectionDomain();\r
217         }catch(SecurityException e){\r
218             return null;\r
219         }\r
220 \r
221         CodeSource src = domain.getCodeSource();\r
222 \r
223         URL location = src.getLocation();\r
224         String scheme = location.getProtocol();\r
225         if( ! scheme.equals(SCHEME_FILE) ) return null;\r
226 \r
227         URI uri;\r
228         try{\r
229             uri = location.toURI();\r
230         }catch(URISyntaxException e){\r
231             assert false;\r
232             return null;\r
233         }\r
234 \r
235         File file = new File(uri);\r
236 \r
237         return file;\r
238     }\r
239 \r
240     /**\r
241      * すでに存在するJARファイルか判定する。\r
242      * @param file 任意のファイル\r
243      * @return すでに存在するJARファイルであればtrue\r
244      */\r
245     public static boolean isExistsJarFile(File file){\r
246         if(file == null) return false;\r
247         if( ! file.exists() ) return false;\r
248         if( ! file.isFile() ) return false;\r
249 \r
250         String name = file.getName();\r
251         if( ! name.matches("^.+\\.[jJ][aA][rR]$") ) return false;\r
252 \r
253         // TODO ファイル先頭マジックナンバーのテストも必要?\r
254 \r
255         return true;\r
256     }\r
257 \r
258     /**\r
259      * クラスがローカルJARファイルからロードされたのであれば\r
260      * その格納ディレクトリを返す。\r
261      * @param klass 任意のクラス\r
262      * @return ロード元JARファイルの格納ディレクトリ。\r
263      * JARが見つからない、もしくはロード元がJARファイルでなければnull。\r
264      */\r
265     public static File getJarDirectory(Class<?> klass){\r
266         File jarFile = getClassSourceFile(klass);\r
267         if(jarFile == null) return null;\r
268 \r
269         if( ! isExistsJarFile(jarFile) ){\r
270             return null;\r
271         }\r
272 \r
273         return jarFile.getParentFile();\r
274     }\r
275 \r
276     /**\r
277      * ホームディレクトリを得る。\r
278      * システムプロパティuser.homeで示されたホームディレクトリを返す。\r
279      * @return ホームディレクトリ。何らかの事情でnullを返す場合もあり。\r
280      */\r
281     public static File getHomeDirectory(){\r
282         String homeProp;\r
283         try{\r
284             homeProp = System.getProperty("user.home");\r
285         }catch(SecurityException e){\r
286             return null;\r
287         }\r
288 \r
289         File homeFile = new File(homeProp);\r
290 \r
291         return homeFile;\r
292     }\r
293 \r
294     /**\r
295      * MacOSX環境か否か判定する。\r
296      * @return MacOSX環境ならtrue\r
297      */\r
298     public static boolean isMacOSXFs(){\r
299         if(File.separatorChar != '/') return false;\r
300 \r
301         String osName;\r
302         try{\r
303             osName = System.getProperty("os.name");\r
304         }catch(SecurityException e){\r
305             return false;\r
306         }\r
307 \r
308         if(osName == null) return false;\r
309 \r
310         osName = osName.toLowerCase(ROOT);\r
311 \r
312         if(osName.startsWith("mac os x")){\r
313             return true;\r
314         }\r
315 \r
316         return false;\r
317     }\r
318 \r
319     /**\r
320      * Windows環境か否か判定する。\r
321      * @return Windows環境ならtrue\r
322      */\r
323     public static boolean isWindowsOSFs(){\r
324         if(File.separatorChar != '\\') return false;\r
325 \r
326         String osName;\r
327         try{\r
328             osName = System.getProperty("os.name");\r
329         }catch(SecurityException e){\r
330             return false;\r
331         }\r
332 \r
333         if(osName == null) return false;\r
334 \r
335         osName = osName.toLowerCase(ROOT);\r
336 \r
337         if(osName.startsWith("windows")){\r
338             return true;\r
339         }\r
340 \r
341         return false;\r
342     }\r
343 \r
344     /**\r
345      * アプリケーション設定ディレクトリを返す。\r
346      * 存在の有無、アクセスの可否は関知しない。\r
347      * @return アプリケーション設定ディレクトリ\r
348      */\r
349     public static File getAppSetDir(){\r
350         File home = getHomeDirectory();\r
351         if(home == null) return null;\r
352 \r
353         File result = home;\r
354 \r
355         if(isMacOSXFs()){\r
356             result = new File(result, "Library");\r
357             result = new File(result, "Application Support");\r
358         }\r
359 \r
360         // TODO Win環境での%APPDATA%サポート\r
361 \r
362         return result;\r
363     }\r
364 \r
365     /**\r
366      * ファイル名を表示するためのJLabel用HTML文字列を生成する。\r
367      * Windows日本語環境では、バックスラッシュ記号が円通貨記号に置換される。\r
368      * @param file 対象ファイル\r
369      * @return HTML文字列断片\r
370      */\r
371     public static String getHtmledFileName(File file){\r
372         String pathName = file.getPath();\r
373 \r
374         Locale locale = Locale.getDefault();\r
375         String lang = locale.getLanguage();\r
376 \r
377         if( FileUtils.isWindowsOSFs() && lang.equals("ja") ){\r
378             pathName = pathName.replace(File.separator, "&yen;");\r
379         }\r
380 \r
381         return "<code>" + pathName + "</code>";\r
382     }\r
383 \r
384     /**\r
385      * 隠しコンストラクタ。\r
386      */\r
387     private FileUtils(){\r
388         assert false;\r
389         throw new AssertionError();\r
390     }\r
391 \r
392 }\r