1 | /* |
2 | * Copyright 2005-2006 The RbUtils Project |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | * |
16 | */ |
17 | |
18 | // $Id: FileTools.java,v 1.5 2006/10/27 22:12:36 moishi Exp $ |
19 | |
20 | package org.ktc.rbutils.api.file; |
21 | |
22 | import java.io.File; |
23 | import java.io.FileNotFoundException; |
24 | import java.io.IOException; |
25 | import java.util.Collection; |
26 | import java.util.Locale; |
27 | import org.apache.commons.io.FileUtils; |
28 | import org.apache.commons.io.FilenameUtils; |
29 | import org.apache.commons.lang.StringUtils; |
30 | import org.apache.commons.lang.Validate; |
31 | import org.ktc.rbutils.api.i18n.LocaleUtils; |
32 | |
33 | /** |
34 | * General file manipulation utilities. |
35 | * @since RbUtils 0.1.0 |
36 | * @version $Revision: 1.5 $ |
37 | * @author redfish |
38 | */ |
39 | public class FileTools { |
40 | /** The file extension of a java file. */ |
41 | public static final String JAVA_EXTENSION = "java"; |
42 | |
43 | /** The extension separator character. */ |
44 | public static final char EXTENSION_SEPARATOR_CHAR = '.'; |
45 | /** The extension separator String. */ |
46 | public static final String EXTENSION_SEPARATOR = String.valueOf(EXTENSION_SEPARATOR_CHAR); |
47 | /** The package separator character. */ |
48 | public static final char PACKAGE_SEPARATOR_CHAR = EXTENSION_SEPARATOR_CHAR; |
49 | /** The package separator String. */ |
50 | public static final String PACKAGE_SEPARATOR = String.valueOf(PACKAGE_SEPARATOR_CHAR); |
51 | |
52 | /** The Unix file separator character. */ |
53 | public static final char UNIX_SEPARATOR_CHAR = '/'; |
54 | /** The Unix file separator String. */ |
55 | public static final String UNIX_SEPARATOR = String.valueOf(UNIX_SEPARATOR_CHAR); |
56 | /** The Dos file separator character. */ |
57 | public static final char DOS_SEPARATOR_CHAR = '\\'; |
58 | /** The Dos file separator String. */ |
59 | public static final String DOS_SEPARATOR = String.valueOf(DOS_SEPARATOR_CHAR); |
60 | |
61 | /** |
62 | * Dummy constructor. This <b>MUST NOT</b> be instanciated. |
63 | * @throws UnsupportedOperationException if this class is instanciated |
64 | */ |
65 | protected FileTools() { |
66 | // prevents calls from subclasses and classes of the package |
67 | throw new UnsupportedOperationException(); |
68 | } |
69 | |
70 | // TODO Design - changer le nom de la méthode |
71 | /** |
72 | * Returns the package (if <code>file</code> is a directory) or class (if <code>file</code> |
73 | * is a file in the filesystem) name of a <code>File</code> according to its relative path |
74 | * from a root directory. |
75 | * <p> |
76 | * The name of this package uses the Java language dot notation for the package. i.e |
77 | * <code>java.lang</code>. <br> |
78 | * It can be the sub-package name (the name of the package which contains this class/package or |
79 | * a full class/package name (name of the sub-package and the name of this class/package). |
80 | * <p> |
81 | * If <code>root</code> and <code>file</code> denote the same directory, , the returned |
82 | * value is the package separator. <br> |
83 | * If <code>file</code> is in the <code>root</code> directory and <code>onlySubPkg</code> |
84 | * is <code>true</code>, the empty string is returned. |
85 | * @param root the root directory used to resolved package or class names against |
86 | * <code>file</code> |
87 | * @param file the file used to resolve its package/class name |
88 | * @param removedLocale if <code>true</code>, the locale is removed from the class name; |
89 | * otherwise, the locale is kept (only used if <code>file</code> is a file in the |
90 | * filesytem or <code>onlySubPkg</code> is false) |
91 | * @param onlySubPkg if <code>true</code>, returns only the sub-package name ; otherwise, |
92 | * returns the full class/package name |
93 | * @return the package name as desired |
94 | * @throws org.apache.commons.lang.NullArgumentException if <code>root</code> or |
95 | * <code>file</code> is <code>null</code> |
96 | * @throws FileNotFoundException if <code>root</code> does not exist |
97 | * @throws NotDirectoryException if <code>root</code> is not a directory |
98 | */ |
99 | public static String inferPackageClassName(final File root, |
100 | final File file, |
101 | final boolean removedLocale, |
102 | final boolean onlySubPkg) |
103 | throws FileNotFoundException |
104 | { |
105 | // Validate arguments |
106 | ValidateFile.isDirectory(root); |
107 | ValidateFile.notNull(file); |
108 | |
109 | String result = null; |
110 | |
111 | // Test if root and file denote the same file |
112 | if (equalNormalized(root, file)) { |
113 | result = PACKAGE_SEPARATOR; |
114 | } |
115 | else { |
116 | final File pathDirectory = file.getParentFile(); |
117 | String subPkgName = StringUtils.EMPTY; |
118 | |
119 | // Test if root and pathDirectory does not denote the same file |
120 | if (!equalNormalized(root, pathDirectory)) { |
121 | final String localPath = getStrippedFileName(root, pathDirectory); |
122 | subPkgName = localPath.replace(File.separatorChar, PACKAGE_SEPARATOR_CHAR); |
123 | } |
124 | |
125 | String classOrPkgName = getNameWithoutExtension(file); |
126 | |
127 | // Optionnally, remove locale from class name |
128 | final boolean isDirectory = file.isDirectory(); |
129 | if (removedLocale && !isDirectory) { |
130 | classOrPkgName = LocaleUtils.removeLocale(classOrPkgName); |
131 | } |
132 | |
133 | if (onlySubPkg) { |
134 | result = subPkgName; |
135 | } |
136 | else if (StringUtils.EMPTY.equals(subPkgName)) { |
137 | result = classOrPkgName; |
138 | } |
139 | else { |
140 | result = subPkgName + PACKAGE_SEPARATOR + classOrPkgName; |
141 | } |
142 | } |
143 | |
144 | return result; |
145 | } |
146 | |
147 | /** |
148 | * Returns the package (if <code>file</code> is a directory) or class (if <code>file</code> |
149 | * is a file in the filesystem) name of a <code>File</code> according to its relative path |
150 | * from a root directory. |
151 | * <p> |
152 | * The name of this package uses the Java language dot notation for the package. i.e |
153 | * <code>java.lang</code>. <br> |
154 | * The returned value is the full class/package name (name of the sub-package and the name of |
155 | * this class/package). |
156 | * @param root the root directory used to resolve package or class names against |
157 | * <code>file</code> |
158 | * @param file the file used to resolved its package/class name |
159 | * @param removedLocale if <code>true</code>, the locale is removed from the class name; |
160 | * otherwise, the locale is kept |
161 | * @return the package name as desired |
162 | * @throws org.apache.commons.lang.NullArgumentException if <code>root</code> or |
163 | * <code>file</code> is <code>null</code> |
164 | * @throws FileNotFoundException if <code>root</code> does not exist |
165 | * @throws NotDirectoryException if <code>root</code> is not a directory |
166 | */ |
167 | public static String inferPackageClassName(final File root, final File file, |
168 | final boolean removedLocale) |
169 | throws FileNotFoundException |
170 | { |
171 | return inferPackageClassName(root, file, removedLocale, false); |
172 | } |
173 | |
174 | /** |
175 | * Returns the package (if <code>file</code> is a directory) or class (if <code>file</code> |
176 | * is a file in the filesystem) name of a <code>File</code> according to its relative path |
177 | * from a root directory. |
178 | * <p> |
179 | * The name of this package uses the Java language dot notation for the package. i.e |
180 | * <code>java.lang</code>. <br> |
181 | * The returned value is the sub-package name (the name of the package which contains this |
182 | * class/package. |
183 | * <p> |
184 | * If <code>file</code> is the <code>root</code> directory , the returned value is the |
185 | * package separator. <br> |
186 | * If <code>file</code> is in the <code>root</code> directory , the returned value is the |
187 | * empty string. |
188 | * @param root the root directory used to resolve package or class names against |
189 | * <code>file</code> |
190 | * @param file the file used to resolved its package/class name |
191 | * @return the package name as desired |
192 | * @throws org.apache.commons.lang.NullArgumentException if <code>root</code> or |
193 | * <code>file</code> is <code>null</code> |
194 | * @throws FileNotFoundException if <code>root</code> does not exist |
195 | * @throws NotDirectoryException if <code>root</code> is not a directory |
196 | */ |
197 | public static String inferPackageName(final File root, final File file) |
198 | throws FileNotFoundException |
199 | { |
200 | return inferPackageClassName(root, file, false, true); |
201 | } |
202 | |
203 | /** |
204 | * Returns the filename of a <code>File</code> without extension. |
205 | * @param file the <code>File</code> from which the name without extension is desired |
206 | * @return the filename without extension |
207 | * @throws org.apache.commons.lang.NullArgumentException if <code>file</code> is |
208 | * <code>null</code> |
209 | */ |
210 | public static String getNameWithoutExtension(final File file) { |
211 | // Validate argument |
212 | ValidateFile.notNull(file); |
213 | |
214 | return FilenameUtils.removeExtension(file.getName()); |
215 | } |
216 | |
217 | /** |
218 | * Create a stripped down version of a filename. |
219 | * @param root the root directory used to resolve the stripped file name against |
220 | * <code>file</code> |
221 | * @param file the file used to resolve its stripped file name |
222 | * @return the filename where an initial basedir is stripped |
223 | * @throws org.apache.commons.lang.NullArgumentException if <code>root</code> or |
224 | * <code>file</code> is <code>null</code> |
225 | * @throws FileNotFoundException if <code>root</code> does not exist |
226 | * @throws NotDirectoryException if <code>root</code> is not a directory |
227 | */ |
228 | public static String getStrippedFileName(final File root, final File file) |
229 | throws FileNotFoundException |
230 | { |
231 | return getStrippedFileName(root, file, false); |
232 | } |
233 | |
234 | /** |
235 | * Create a stripped down version of a filename. |
236 | * <p> |
237 | * If file is in root, returns an empty <code>String</code>. |
238 | * @param root the root directory used to resolved the stripped file name against |
239 | * <code>file</code> |
240 | * @param file the file used to resolve its stripped file name |
241 | * @param forceUnixSeparator if <code>true</code>, the returned value uses the |
242 | * {@link FileTools#UNIX_SEPARATOR_CHAR} as file separator; otherwise, it uses the |
243 | * system file separator |
244 | * @return the filename where an initial basedir is stripped |
245 | * @throws org.apache.commons.lang.NullArgumentException if <code>root</code> or |
246 | * <code>file</code> is <code>null</code> |
247 | * @throws FileNotFoundException if <code>root</code> does not exist |
248 | * @throws NotDirectoryException if <code>root</code> is not a directory |
249 | * @throws NotRootParentOfFileException if <code>file</code> is not in <code>root</code> |
250 | */ |
251 | public static String getStrippedFileName(final File root, final File file, |
252 | final boolean forceUnixSeparator) |
253 | throws FileNotFoundException |
254 | { |
255 | String stripped; |
256 | |
257 | // Validate arguments |
258 | ValidateFile.isDirectory(root); |
259 | ValidateFile.notNull(file); |
260 | |
261 | if (!isRootParentOfFile(root, file)) { |
262 | throw new NotRootParentOfFileException(root, file); |
263 | } |
264 | |
265 | // file and root denote the same file in the filesystem |
266 | if (equalNormalized(root, file)) { |
267 | stripped = StringUtils.EMPTY; |
268 | } |
269 | else { |
270 | final String rootPath = normalizeNoEndSeparator(root); |
271 | final String filePath = normalizeNoEndSeparator(file); |
272 | stripped = filePath.substring(rootPath.length() + 1); |
273 | } |
274 | |
275 | if (forceUnixSeparator) { |
276 | stripped = stripped.replace(File.separatorChar, UNIX_SEPARATOR_CHAR); |
277 | } |
278 | |
279 | return stripped; |
280 | } |
281 | |
282 | /** |
283 | * Determines if <code>file</code> is in the <code>root</code> directory. |
284 | * @param root the directory |
285 | * @param file the file (as file in the file system) |
286 | * @return <code>true</code> if <code>file</code> is in the <code>root</code> directory; |
287 | * otherwise <code>false</code> |
288 | * @throws org.apache.commons.lang.NullArgumentException if <code>root</code> or |
289 | * <code>file</code> is <code>null</code> |
290 | * @throws FileNotFoundException if <code>root</code> does not exist |
291 | * @throws NotDirectoryException if <code>root</code> is not a directory |
292 | */ |
293 | public static boolean isRootParentOfFile(final File root, final File file) |
294 | throws FileNotFoundException |
295 | { |
296 | boolean isParent = false; |
297 | // Validate arguments |
298 | ValidateFile.isDirectory(root); |
299 | ValidateFile.notNull(file); |
300 | |
301 | final String rootPath = normalizeNoEndSeparator(root); |
302 | final String filePath = normalizeNoEndSeparator(file); |
303 | |
304 | if (filePath.startsWith(rootPath)) { |
305 | isParent = true; |
306 | } |
307 | |
308 | return isParent; |
309 | } |
310 | |
311 | /** |
312 | * Returns the locale of the <code>file</code> according to its filename. <b>Note:</b> this |
313 | * method will work as expected if and only if the filename of the <code>File</code> does not |
314 | * contain the underscore character '_' (except for the locale part). |
315 | * @param file file from which the locale is desired |
316 | * @return the locale of the file according to the filename |
317 | * @throws org.apache.commons.lang.NullArgumentException if <code>file</code> is |
318 | * <code>null</code> |
319 | * @throws FileNotFoundException if <code>file</code> does not exist |
320 | * @throws org.ktc.rbutils.api.file.NotFileException if <code>file</code> is not a file in the |
321 | * file system |
322 | */ |
323 | public static Locale getLocaleFrom(final File file) throws FileNotFoundException { |
324 | final Locale locale; |
325 | // Validate argument |
326 | ValidateFile.isFile(file); |
327 | |
328 | // Gets the locale from the file name |
329 | final String fileName = getNameWithoutExtension(file); |
330 | locale = LocaleUtils.getLocaleFrom(fileName); |
331 | |
332 | return locale; |
333 | } |
334 | |
335 | // A VOIR Design - c'est pas ca qu'on veut, on veut ca pour le nom d'une classe???? |
336 | // public static File getBaseLocaleFile(final File file) { |
337 | // // Validate argument |
338 | // Validate.notNull(file); |
339 | // |
340 | // final File baseLocaleFile; |
341 | // final File parent = file.getParentFile(); |
342 | // final String fileName = file.getName(); |
343 | // final String extension = FilenameUtils.getExtension(fileName); |
344 | // |
345 | // // A VOIR Code - cas où il n'y a pas de locale sep!!! |
346 | // final int localeSepPosition = fileName.indexOf(LocaleUtils.LOCALE_SEPARATOR_CHAR); |
347 | // if (localeSepPosition == 0) { |
348 | // baseLocaleFile = getNewInstance(file); |
349 | // } |
350 | // else { |
351 | // final String baseLocaleFileName = fileName.substring(0, localeSepPosition); |
352 | // baseLocaleFile = new File(parent, baseLocaleFileName + EXTENSION_SEPARATOR + extension); |
353 | // } |
354 | // |
355 | // return baseLocaleFile; |
356 | // } |
357 | |
358 | // RFE doit affecter tous les paramètre du file passer en paramètre |
359 | /** |
360 | * Get a new instance of a <code>File</code>. |
361 | * @param file a file from which a new instance is desired |
362 | * @return the new instance |
363 | * @throws org.apache.commons.lang.NullArgumentException if <code>file</code> is |
364 | * <code>null</code> |
365 | */ |
366 | public static File getNewInstance(final File file) { |
367 | // Validate argument |
368 | ValidateFile.notNull(file); |
369 | |
370 | return new File(file.getAbsolutePath()); |
371 | } |
372 | |
373 | // /** |
374 | // * Remove the last file separator of a filename if present. |
375 | // * @param filename the filename to be processed |
376 | // * @return the filename without the last file separator if present |
377 | // * @throws IllegalArgumentException if filename is <code>null</code> |
378 | // */ |
379 | // public static String removeLastFileSeparator(final String filename) { |
380 | // // Validate arguments |
381 | // Validate.notNull(filename); |
382 | // |
383 | // final int foundSep; |
384 | // if (filename.endsWith(UNIX_SEPARATOR) || filename.endsWith(DOS_SEPARATOR)) { |
385 | // foundSep = 1; |
386 | // } |
387 | // else { |
388 | // foundSep = 0; |
389 | // } |
390 | // |
391 | // return filename.substring(0, filename.length() - foundSep); |
392 | // } |
393 | |
394 | /** |
395 | * Checks whether two <code>File</code> are equal after both have been normalized and using |
396 | * the case rules of the system. |
397 | * <p> |
398 | * Both filenames are first passed to {@link #normalizeNoEndSeparator(File)}. The check is then |
399 | * performed case-sensitive on Unix and case-insensitive on Windows. |
400 | * @param file1 the first file to query |
401 | * @param file2 the second file to query |
402 | * @return <code>true</code> if the two <code>File</code> are equals; <code>false</code> |
403 | * otherwise |
404 | * @throws org.apache.commons.lang.NullArgumentException if <code>file1</code> or |
405 | * <code>file2</code> is <code>null</code> |
406 | */ |
407 | public static boolean equalNormalized(final File file1, final File file2) { |
408 | // We need to remove the last file separator because equalsNormalizedOnSystem does not do it |
409 | final String filename1 = normalizeNoEndSeparator(file1); |
410 | final String filename2 = normalizeNoEndSeparator(file2); |
411 | |
412 | return FilenameUtils.equalsNormalizedOnSystem(filename1, filename2); |
413 | } |
414 | |
415 | /** |
416 | * Normalizes the absolute path of a <code>File</code> with the |
417 | * {org.apache.commons.io.FilenameUtils#normalizeNoEndSeparator(java.lang.String) |
418 | * normalizeNoEndSeparator()} method. |
419 | * @param file the file to be normalized. |
420 | * @return the normalized absolute path. |
421 | * @throws org.apache.commons.lang.NullArgumentException if <code>file</code> is |
422 | * <code>null</code> |
423 | * @see org.apache.commons.io.FilenameUtils#normalizeNoEndSeparator(java.lang.String) |
424 | */ |
425 | public static String normalizeNoEndSeparator(final File file) { |
426 | // Validate argument |
427 | ValidateFile.notNull(file); |
428 | |
429 | return FilenameUtils.normalizeNoEndSeparator(file.getAbsolutePath()); |
430 | } |
431 | |
432 | /** |
433 | * Returns the number of files found in a directory. |
434 | * @param directory the directory to search in. |
435 | * @param extensions an array of extensions, ex. {"java","xml"}. If this parameter is null, all |
436 | * files are accepted. |
437 | * @param recursive If <code>true</code> all subdirectories are searched, too. |
438 | * @return the number of found files. |
439 | * @throws org.apache.commons.lang.NullArgumentException if <code>directory</code> is |
440 | * <code>null</code> |
441 | * @throws FileNotFoundException if <code>directory</code> does not exist |
442 | * @throws NotDirectoryException if <code>directory</code> is not a directory |
443 | * @since RbUtils 0.9.3.3 |
444 | */ |
445 | public static int countFiles(final File directory, final String[] extensions, |
446 | final boolean recursive) throws FileNotFoundException |
447 | { |
448 | // TODO Test - to be unit tested |
449 | ValidateFile.isDirectory(directory); |
450 | final Collection list = FileUtils.listFiles(directory, extensions, recursive); |
451 | return list.size(); |
452 | } |
453 | |
454 | /** |
455 | * Cleans and creates a new file in the file system. |
456 | * <p> |
457 | * The previous file is deleted if it exists. All parent directories of the file are first |
458 | * created. Finally, a new file is created. |
459 | * @param file the file to be created. |
460 | * @throws IOException if an error occurs on file deletion, on file creation, or on parent |
461 | * directories creation. |
462 | */ |
463 | public static void forceNewFile(final File file) throws IOException { |
464 | Validate.notNull(file); |
465 | if (file.exists()) { |
466 | FileUtils.forceDelete(file); |
467 | } |
468 | final File parentDirectory = file.getParentFile(); |
469 | parentDirectory.mkdirs(); |
470 | FileUtils.forceMkdir(parentDirectory); |
471 | final boolean creationState = file.createNewFile(); |
472 | if (!creationState) { |
473 | throw new IOException(file.getAbsolutePath() + " cannot be created"); |
474 | } |
475 | } |
476 | |
477 | } |