Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
FileUtils |
|
| 0.0;0 | ||||
FileUtils$FilterWrapper |
|
| 0.0;0 |
1 | package org.apache.maven.shared.utils.io; | |
2 | ||
3 | /* | |
4 | * Licensed to the Apache Software Foundation (ASF) under one | |
5 | * or more contributor license agreements. See the NOTICE file | |
6 | * distributed with this work for additional information | |
7 | * regarding copyright ownership. The ASF licenses this file | |
8 | * to you under the Apache License, Version 2.0 (the | |
9 | * "License"); you may not use this file except in compliance | |
10 | * with the License. You may obtain a copy of the License at | |
11 | * | |
12 | * http://www.apache.org/licenses/LICENSE-2.0 | |
13 | * | |
14 | * Unless required by applicable law or agreed to in writing, | |
15 | * software distributed under the License is distributed on an | |
16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
17 | * KIND, either express or implied. See the License for the | |
18 | * specific language governing permissions and limitations | |
19 | * under the License. | |
20 | */ | |
21 | ||
22 | import java.io.BufferedReader; | |
23 | import java.io.File; | |
24 | import java.io.FileInputStream; | |
25 | import java.io.FileOutputStream; | |
26 | import java.io.FileReader; | |
27 | import java.io.FileWriter; | |
28 | import java.io.IOException; | |
29 | import java.io.InputStream; | |
30 | import java.io.InputStreamReader; | |
31 | import java.io.OutputStream; | |
32 | import java.io.OutputStreamWriter; | |
33 | import java.io.Reader; | |
34 | import java.io.Writer; | |
35 | import java.net.URL; | |
36 | import java.nio.channels.FileChannel; | |
37 | import java.security.SecureRandom; | |
38 | import java.text.DecimalFormat; | |
39 | import java.util.ArrayList; | |
40 | import java.util.Arrays; | |
41 | import java.util.Collections; | |
42 | import java.util.List; | |
43 | import java.util.Random; | |
44 | ||
45 | import org.apache.maven.shared.utils.Os; | |
46 | import org.apache.maven.shared.utils.StringUtils; | |
47 | ||
48 | import javax.annotation.Nonnull; | |
49 | import javax.annotation.Nullable; | |
50 | import javax.annotation.WillClose; | |
51 | ||
52 | /** | |
53 | * This class provides basic facilities for manipulating files and file paths. | |
54 | * <p/> | |
55 | * <h3>Path-related methods</h3> | |
56 | * <p/> | |
57 | * <p>Methods exist to retrieve the components of a typical file path. For example | |
58 | * <code>/www/hosted/mysite/index.html</code>, can be broken into: | |
59 | * <ul> | |
60 | * <li><code>/www/hosted/mysite/index</code> -- retrievable through {@link #removeExtension}</li> | |
61 | * <li><code>html</code> -- retrievable through {@link #getExtension}</li> | |
62 | * </ul> | |
63 | * </p> | |
64 | * <p/> | |
65 | * <h3>File-related methods</h3> | |
66 | * <p/> | |
67 | * There are methods to create a {@link #toFile File from a URL}, copy a | |
68 | * copy a {@link #copyFile File to another File}, | |
69 | * copy a {@link #copyURLToFile URL's contents to a File}, | |
70 | * as well as methods to {@link #deleteDirectory(File) delete} and {@link #cleanDirectory(File) | |
71 | * clean} a directory. | |
72 | * </p> | |
73 | * <p/> | |
74 | * Common {@link java.io.File} manipulation routines. | |
75 | * <p/> | |
76 | * Taken from the commons-utils repo. | |
77 | * Also code from Alexandria's FileUtils. | |
78 | * And from Avalon Excalibur's IO. | |
79 | * And from Ant. | |
80 | * | |
81 | * @author <a href="mailto:burton@relativity.yi.org">Kevin A. Burton</A> | |
82 | * @author <a href="mailto:sanders@apache.org">Scott Sanders</a> | |
83 | * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a> | |
84 | * @author <a href="mailto:Christoph.Reck@dlr.de">Christoph.Reck</a> | |
85 | * @author <a href="mailto:peter@apache.org">Peter Donald</a> | |
86 | * @author <a href="mailto:jefft@apache.org">Jeff Turner</a> | |
87 | * @version $Id: FileUtils.java 1401850 2012-10-24 19:59:48Z rfscholte $ | |
88 | */ | |
89 | public class FileUtils | |
90 | { | |
91 | protected FileUtils() | |
92 | 0 | { |
93 | // This is a utility class. Normally dont instantiate | |
94 | 0 | } |
95 | ||
96 | /** | |
97 | * The number of bytes in a kilobyte. | |
98 | */ | |
99 | private static final int ONE_KB = 1024; | |
100 | ||
101 | /** | |
102 | * The number of bytes in a megabyte. | |
103 | */ | |
104 | private static final int ONE_MB = ONE_KB * ONE_KB; | |
105 | ||
106 | /** | |
107 | * The number of bytes in a gigabyte. | |
108 | */ | |
109 | private static final int ONE_GB = ONE_KB * ONE_MB; | |
110 | ||
111 | /** | |
112 | * The file copy buffer size (30 MB) | |
113 | */ | |
114 | private static final long FILE_COPY_BUFFER_SIZE = ONE_MB * 30; | |
115 | ||
116 | /** | |
117 | * The vm line separator | |
118 | */ | |
119 | 1 | private static final String FS = System.getProperty( "file.separator" ); |
120 | ||
121 | /** | |
122 | * Non-valid Characters for naming files, folders under Windows: <code>":", "*", "?", "\"", "<", ">", "|"</code> | |
123 | * | |
124 | * @see <a href="http://support.microsoft.com/?scid=kb%3Ben-us%3B177506&x=12&y=13"> | |
125 | * http://support.microsoft.com/?scid=kb%3Ben-us%3B177506&x=12&y=13</a> | |
126 | */ | |
127 | 1 | private static final String[] INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME = { ":", "*", "?", "\"", "<", ">", "|" }; |
128 | ||
129 | /** | |
130 | * @return the default excludes pattern | |
131 | * @see DirectoryScanner#DEFAULTEXCLUDES | |
132 | */ | |
133 | public @Nonnull static String[] getDefaultExcludes() | |
134 | { | |
135 | 2 | return DirectoryScanner.DEFAULTEXCLUDES; |
136 | } | |
137 | ||
138 | /** | |
139 | * @return the default excludes pattern as list. | |
140 | * @see #getDefaultExcludes() | |
141 | */ | |
142 | @Nonnull public static List<String> getDefaultExcludesAsList() | |
143 | { | |
144 | 1 | return Arrays.asList( getDefaultExcludes() ); |
145 | } | |
146 | ||
147 | /** | |
148 | * @return the default excludes pattern as comma separated string. | |
149 | * @see DirectoryScanner#DEFAULTEXCLUDES | |
150 | * @see StringUtils#join(Object[], String) | |
151 | */ | |
152 | public @Nonnull static String getDefaultExcludesAsString() | |
153 | { | |
154 | 1 | return StringUtils.join( DirectoryScanner.DEFAULTEXCLUDES, "," ); |
155 | } | |
156 | ||
157 | /** | |
158 | * Returns the directory path portion of a file specification string. | |
159 | * Matches the equally named unix command. | |
160 | * | |
161 | * @param filename the file path | |
162 | * @return The directory portion excluding the ending file separator. | |
163 | */ | |
164 | public @Nonnull static String dirname( @Nonnull String filename ) | |
165 | { | |
166 | 7 | int i = filename.lastIndexOf( File.separator ); |
167 | 6 | return ( i >= 0 ? filename.substring( 0, i ) : "" ); |
168 | } | |
169 | ||
170 | /** | |
171 | * Returns the filename portion of a file specification string. | |
172 | * | |
173 | * @param filename the file path | |
174 | * @return The filename string with extension. | |
175 | */ | |
176 | public @Nonnull static String filename( @Nonnull String filename ) | |
177 | { | |
178 | 7 | int i = filename.lastIndexOf( File.separator ); |
179 | 6 | return ( i >= 0 ? filename.substring( i + 1 ) : filename ); |
180 | } | |
181 | ||
182 | /** | |
183 | * Returns the extension portion of a file specification string. | |
184 | * This everything after the last dot '.' in the filename (NOT including | |
185 | * the dot). | |
186 | * | |
187 | * @param filename the file path | |
188 | * @return the extension of the file | |
189 | */ | |
190 | public @Nonnull static String extension( @Nonnull String filename ) | |
191 | { | |
192 | // Ensure the last dot is after the last file separator | |
193 | 8 | int lastSep = filename.lastIndexOf( File.separatorChar ); |
194 | int lastDot; | |
195 | 7 | if ( lastSep < 0 ) |
196 | { | |
197 | 5 | lastDot = filename.lastIndexOf( '.' ); |
198 | } | |
199 | else | |
200 | { | |
201 | 2 | lastDot = filename.substring( lastSep + 1 ).lastIndexOf( '.' ); |
202 | 2 | if ( lastDot >= 0 ) |
203 | { | |
204 | 2 | lastDot += lastSep + 1; |
205 | } | |
206 | } | |
207 | ||
208 | 7 | if ( lastDot >= 0 && lastDot > lastSep ) |
209 | { | |
210 | 5 | return filename.substring( lastDot + 1 ); |
211 | } | |
212 | ||
213 | 2 | return ""; |
214 | } | |
215 | ||
216 | /** | |
217 | * Check if a file exits. | |
218 | * | |
219 | * @param fileName the file path. | |
220 | * @return true if file exists. | |
221 | */ | |
222 | public static boolean fileExists( @Nonnull String fileName ) | |
223 | { | |
224 | 0 | File file = new File( fileName ); |
225 | 0 | return file.exists(); |
226 | } | |
227 | ||
228 | /** | |
229 | * Note: the file content is read with platform encoding. | |
230 | * | |
231 | * @param file the file path | |
232 | * @return the file content using the platform encoding. | |
233 | * @throws IOException if any | |
234 | */ | |
235 | public @Nonnull static String fileRead( @Nonnull String file ) | |
236 | throws IOException | |
237 | { | |
238 | 0 | return fileRead( file, null ); |
239 | } | |
240 | ||
241 | /** | |
242 | * @param file the file path | |
243 | * @param encoding the wanted encoding | |
244 | * @return the file content using the specified encoding. | |
245 | * @throws IOException if any | |
246 | */ | |
247 | private @Nonnull static String fileRead( @Nonnull String file, @Nullable String encoding ) | |
248 | throws IOException | |
249 | { | |
250 | 0 | return fileRead( new File( file ), encoding ); |
251 | } | |
252 | ||
253 | /** | |
254 | * Note: the file content is read with platform encoding | |
255 | * | |
256 | * @param file the file path | |
257 | * @return the file content using the platform encoding. | |
258 | * @throws IOException if any | |
259 | */ | |
260 | public @Nonnull static String fileRead( @Nonnull File file ) | |
261 | throws IOException | |
262 | { | |
263 | 5 | return fileRead( file, null ); |
264 | } | |
265 | ||
266 | /** | |
267 | * @param file the file path | |
268 | * @param encoding the wanted encoding | |
269 | * @return the file content using the specified encoding. | |
270 | * @throws IOException if any | |
271 | */ | |
272 | public @Nonnull static String fileRead( @Nonnull File file, @Nullable String encoding ) | |
273 | throws IOException | |
274 | { | |
275 | 8 | StringBuilder buf = new StringBuilder(); |
276 | ||
277 | 8 | Reader reader = null; |
278 | ||
279 | try | |
280 | { | |
281 | 8 | if ( encoding != null ) |
282 | { | |
283 | 3 | reader = new InputStreamReader( new FileInputStream( file ), encoding ); |
284 | } | |
285 | else | |
286 | { | |
287 | 5 | reader = new InputStreamReader( new FileInputStream( file ) ); |
288 | } | |
289 | int count; | |
290 | 8 | char[] b = new char[512]; |
291 | 16 | while ( ( count = reader.read( b ) ) > 0 ) // blocking read |
292 | { | |
293 | 8 | buf.append( b, 0, count ); |
294 | } | |
295 | } | |
296 | finally | |
297 | { | |
298 | 8 | IOUtil.close( reader ); |
299 | 8 | } |
300 | ||
301 | 8 | return buf.toString(); |
302 | } | |
303 | ||
304 | /** | |
305 | * @param file the file path | |
306 | * @return the file content lines as String[] using the systems default encoding. | |
307 | * An empty List if the file didn't exist. | |
308 | * @throws IOException | |
309 | */ | |
310 | public @Nonnull static String[] fileReadArray( @Nonnull File file ) | |
311 | throws IOException | |
312 | { | |
313 | 0 | List<String> files = loadFile( file ); |
314 | ||
315 | 0 | return files.toArray( new String[files.size()] ); |
316 | } | |
317 | ||
318 | /** | |
319 | * Appends data to a file. The file will be created if it does not exist. | |
320 | * Note: the data is written with platform encoding | |
321 | * | |
322 | * @param fileName The path of the file to write. | |
323 | * @param data The content to write to the file. | |
324 | * @throws IOException if any | |
325 | */ | |
326 | public static void fileAppend( @Nonnull String fileName, @Nonnull String data ) | |
327 | throws IOException | |
328 | { | |
329 | 3 | fileAppend( fileName, null, data ); |
330 | 3 | } |
331 | ||
332 | /** | |
333 | * Appends data to a file. The file will be created if it does not exist. | |
334 | * | |
335 | * @param fileName The path of the file to write. | |
336 | * @param encoding The encoding of the file. | |
337 | * @param data The content to write to the file. | |
338 | * @throws IOException if any | |
339 | */ | |
340 | public static void fileAppend( @Nonnull String fileName, @Nullable String encoding, @Nonnull String data ) | |
341 | throws IOException | |
342 | { | |
343 | 4 | FileOutputStream out = null; |
344 | try | |
345 | { | |
346 | 4 | out = new FileOutputStream( fileName, true ); |
347 | 4 | if ( encoding != null ) |
348 | { | |
349 | 1 | out.write( data.getBytes( encoding ) ); |
350 | } | |
351 | else | |
352 | { | |
353 | 3 | out.write( data.getBytes() ); |
354 | } | |
355 | } | |
356 | finally | |
357 | { | |
358 | 4 | IOUtil.close( out ); |
359 | 4 | } |
360 | 4 | } |
361 | ||
362 | /** | |
363 | * Writes data to a file. The file will be created if it does not exist. | |
364 | * Note: the data is written with platform encoding | |
365 | * | |
366 | * @param fileName The path of the file to write. | |
367 | * @param data The content to write to the file. | |
368 | * @throws IOException if any | |
369 | */ | |
370 | public static void fileWrite( @Nonnull String fileName, @Nonnull String data ) | |
371 | throws IOException | |
372 | { | |
373 | 0 | fileWrite( fileName, null, data ); |
374 | 0 | } |
375 | ||
376 | /** | |
377 | * Writes data to a file. The file will be created if it does not exist. | |
378 | * | |
379 | * @param fileName The path of the file to write. | |
380 | * @param encoding The encoding of the file. | |
381 | * @param data The content to write to the file. | |
382 | * @throws IOException if any | |
383 | */ | |
384 | public static void fileWrite( @Nonnull String fileName, @Nullable String encoding, @Nonnull String data ) | |
385 | throws IOException | |
386 | { | |
387 | 0 | File file = new File( fileName ); |
388 | 0 | fileWrite( file, encoding, data ); |
389 | 0 | } |
390 | ||
391 | /** | |
392 | * Writes data to a file. The file will be created if it does not exist. | |
393 | * | |
394 | * @param file The path of the file to write. | |
395 | * @param encoding The encoding of the file. | |
396 | * @param data The content to write to the file. | |
397 | * @throws IOException if any | |
398 | * | |
399 | */ | |
400 | public static void fileWrite( @Nonnull File file, @Nullable String encoding, @Nonnull String data ) | |
401 | throws IOException | |
402 | { | |
403 | 9 | Writer writer = null; |
404 | try | |
405 | { | |
406 | 9 | OutputStream out = new FileOutputStream( file ); |
407 | 9 | if ( encoding != null ) |
408 | { | |
409 | 4 | writer = new OutputStreamWriter( out, encoding ); |
410 | } | |
411 | else | |
412 | { | |
413 | 5 | writer = new OutputStreamWriter( out ); |
414 | } | |
415 | 9 | writer.write( data ); |
416 | } | |
417 | finally | |
418 | { | |
419 | 9 | IOUtil.close( writer ); |
420 | 9 | } |
421 | 9 | } |
422 | ||
423 | /** | |
424 | * Writes String array data to a file in the systems default encoding. | |
425 | * The file will be created if it does not exist. | |
426 | * | |
427 | * @param file The path of the file to write. | |
428 | * @param data The content to write to the file. | |
429 | * @throws IOException if any | |
430 | * | |
431 | */ | |
432 | public static void fileWriteArray( @Nonnull File file, @Nullable String... data ) | |
433 | throws IOException | |
434 | { | |
435 | 1 | fileWriteArray( file, null, data ); |
436 | 1 | } |
437 | ||
438 | /** | |
439 | * Writes String array data to a file. The file will be created if it does not exist. | |
440 | * | |
441 | * @param file The path of the file to write. | |
442 | * @param encoding The encoding of the file. | |
443 | * @param data The content to write to the file. | |
444 | * @throws IOException if any | |
445 | * | |
446 | */ | |
447 | public static void fileWriteArray( @Nonnull File file, @Nullable String encoding, @Nullable String... data ) | |
448 | throws IOException | |
449 | { | |
450 | 2 | Writer writer = null; |
451 | try | |
452 | { | |
453 | 2 | OutputStream out = new FileOutputStream( file ); |
454 | 2 | if ( encoding != null ) |
455 | { | |
456 | 1 | writer = new OutputStreamWriter( out, encoding ); |
457 | } | |
458 | else | |
459 | { | |
460 | 1 | writer = new OutputStreamWriter( out ); |
461 | } | |
462 | ||
463 | 8 | for ( int i = 0; data != null && i < data.length; i++ ) |
464 | { | |
465 | 6 | writer.write( data[i] ); |
466 | 6 | if ( i < data.length ) |
467 | { | |
468 | 6 | writer.write( "\n" ); |
469 | } | |
470 | } | |
471 | } | |
472 | finally | |
473 | { | |
474 | 2 | IOUtil.close( writer ); |
475 | 2 | } |
476 | 2 | } |
477 | ||
478 | /** | |
479 | * Deletes a file. | |
480 | * | |
481 | * @param fileName The path of the file to delete. | |
482 | */ | |
483 | public static void fileDelete( @Nonnull String fileName ) | |
484 | { | |
485 | 0 | File file = new File( fileName ); |
486 | //noinspection ResultOfMethodCallIgnored | |
487 | 0 | file.delete(); |
488 | 0 | } |
489 | ||
490 | /** | |
491 | * Given a directory and an array of extensions return an array of compliant files. | |
492 | * <p/> | |
493 | * TODO Should an ignore list be passed in? | |
494 | * TODO Should a recurse flag be passed in? | |
495 | * <p/> | |
496 | * The given extensions should be like "java" and not like ".java" | |
497 | * | |
498 | * @param directory The path of the directory. | |
499 | * @param extensions an array of expected extensions. | |
500 | * @return An array of files for the wanted extensions. | |
501 | */ | |
502 | public static String[] getFilesFromExtension( @Nonnull String directory, @Nonnull String... extensions ) | |
503 | { | |
504 | 0 | List<String> files = new ArrayList<String>(); |
505 | ||
506 | 0 | File currentDir = new File( directory ); |
507 | ||
508 | 0 | String[] unknownFiles = currentDir.list(); |
509 | ||
510 | 0 | if ( unknownFiles == null ) |
511 | { | |
512 | 0 | return new String[0]; |
513 | } | |
514 | ||
515 | 0 | for ( String unknownFile : unknownFiles ) |
516 | { | |
517 | 0 | String currentFileName = directory + System.getProperty( "file.separator" ) + unknownFile; |
518 | 0 | File currentFile = new File( currentFileName ); |
519 | ||
520 | 0 | if ( currentFile.isDirectory() ) |
521 | { | |
522 | // ignore all CVS directories... | |
523 | 0 | if ( currentFile.getName().equals( "CVS" ) ) |
524 | { | |
525 | 0 | continue; |
526 | } | |
527 | ||
528 | // ok... transverse into this directory and get all the files... then combine | |
529 | // them with the current list. | |
530 | ||
531 | 0 | String[] fetchFiles = getFilesFromExtension( currentFileName, extensions ); |
532 | 0 | files = blendFilesToList( files, fetchFiles ); |
533 | 0 | } |
534 | else | |
535 | { | |
536 | // ok... add the file | |
537 | ||
538 | 0 | String add = currentFile.getAbsolutePath(); |
539 | 0 | if ( isValidFile( add, extensions ) ) |
540 | { | |
541 | 0 | files.add( add ); |
542 | } | |
543 | } | |
544 | } | |
545 | ||
546 | // ok... move the Vector into the files list... | |
547 | 0 | String[] foundFiles = new String[files.size()]; |
548 | 0 | files.toArray( foundFiles ); |
549 | ||
550 | 0 | return foundFiles; |
551 | } | |
552 | ||
553 | /** | |
554 | * Private helper method for getFilesFromExtension() | |
555 | */ | |
556 | private @Nonnull static List<String> blendFilesToList( @Nonnull List<String> v, @Nonnull String...files ) | |
557 | { | |
558 | 0 | Collections.addAll( v, files ); |
559 | ||
560 | 0 | return v; |
561 | } | |
562 | ||
563 | /** | |
564 | * Checks to see if a file is of a particular type(s). | |
565 | * Note that if the file does not have an extension, an empty string | |
566 | * ("") is matched for. | |
567 | */ | |
568 | private static boolean isValidFile( @Nonnull String file, @Nonnull String... extensions ) | |
569 | { | |
570 | 0 | String extension = extension( file ); |
571 | ||
572 | // ok.. now that we have the "extension" go through the current know | |
573 | // excepted extensions and determine if this one is OK. | |
574 | ||
575 | 0 | for ( String extension1 : extensions ) |
576 | { | |
577 | 0 | if ( extension1.equals( extension ) ) |
578 | { | |
579 | 0 | return true; |
580 | } | |
581 | } | |
582 | ||
583 | 0 | return false; |
584 | ||
585 | } | |
586 | ||
587 | /** | |
588 | * Simple way to make a directory | |
589 | * | |
590 | * @param dir the directory to create | |
591 | * @throws IllegalArgumentException if the dir contains illegal Windows characters under Windows OS. | |
592 | * @see #INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME | |
593 | */ | |
594 | public static void mkdir( @Nonnull String dir ) | |
595 | { | |
596 | 0 | File file = new File( dir ); |
597 | ||
598 | 0 | if ( Os.isFamily( Os.FAMILY_WINDOWS ) && !isValidWindowsFileName( file ) ) |
599 | { | |
600 | 0 | throw new IllegalArgumentException( "The file (" + dir |
601 | + ") cannot contain any of the following characters: \n" | |
602 | + StringUtils.join( INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME, " " ) ); | |
603 | } | |
604 | ||
605 | 0 | if ( !file.exists() ) |
606 | { | |
607 | //noinspection ResultOfMethodCallIgnored | |
608 | 0 | file.mkdirs(); |
609 | } | |
610 | 0 | } |
611 | ||
612 | /** | |
613 | * Compare the contents of two files to determine if they are equal or not. | |
614 | * | |
615 | * @param file1 the first file | |
616 | * @param file2 the second file | |
617 | * @return true if the content of the files are equal or they both don't exist, false otherwise | |
618 | * @throws IOException if any | |
619 | */ | |
620 | public static boolean contentEquals( @Nonnull final File file1, @Nonnull final File file2 ) | |
621 | throws IOException | |
622 | { | |
623 | 13 | final boolean file1Exists = file1.exists(); |
624 | 13 | if ( file1Exists != file2.exists() ) |
625 | { | |
626 | 0 | return false; |
627 | } | |
628 | ||
629 | 13 | if ( !file1Exists ) |
630 | { | |
631 | // two not existing files are equal | |
632 | 4 | return true; |
633 | } | |
634 | ||
635 | 9 | if ( file1.isDirectory() || file2.isDirectory() ) |
636 | { | |
637 | // don't want to compare directory contents | |
638 | 1 | return false; |
639 | } | |
640 | ||
641 | 8 | InputStream input1 = null; |
642 | 8 | InputStream input2 = null; |
643 | try | |
644 | { | |
645 | 8 | input1 = new FileInputStream( file1 ); |
646 | 8 | input2 = new FileInputStream( file2 ); |
647 | 8 | return IOUtil.contentEquals( input1, input2 ); |
648 | ||
649 | } | |
650 | finally | |
651 | { | |
652 | 8 | IOUtil.close( input1 ); |
653 | 8 | IOUtil.close( input2 ); |
654 | } | |
655 | } | |
656 | ||
657 | /** | |
658 | * Convert from a <code>URL</code> to a <code>File</code>. | |
659 | * | |
660 | * @param url File URL. | |
661 | * @return The equivalent <code>File</code> object, or <code>null</code> if the URL's protocol | |
662 | * is not <code>file</code> | |
663 | */ | |
664 | public @Nullable static File toFile( final @Nullable URL url ) | |
665 | { | |
666 | 7 | if ( url == null || !url.getProtocol().equalsIgnoreCase( "file" ) ) |
667 | { | |
668 | 2 | return null; |
669 | } | |
670 | ||
671 | 5 | String filename = url.getFile().replace( '/', File.separatorChar ); |
672 | 5 | int pos = -1; |
673 | 22 | while ( ( pos = filename.indexOf( '%', pos + 1 ) ) >= 0 ) |
674 | { | |
675 | 18 | if ( pos + 2 < filename.length() ) |
676 | { | |
677 | 18 | String hexStr = filename.substring( pos + 1, pos + 3 ); |
678 | 18 | char ch = (char) Integer.parseInt( hexStr, 16 ); |
679 | 17 | filename = filename.substring( 0, pos ) + ch + filename.substring( pos + 3 ); |
680 | 17 | } |
681 | } | |
682 | 4 | return new File( filename ); |
683 | } | |
684 | ||
685 | /** | |
686 | * Convert the array of Files into a list of URLs. | |
687 | * | |
688 | * @param files the array of files | |
689 | * @return the array of URLs | |
690 | * @throws IOException if an error occurs | |
691 | */ | |
692 | public @Nonnull static URL[] toURLs( @Nonnull final File... files ) | |
693 | throws IOException | |
694 | { | |
695 | 1 | final URL[] urls = new URL[files.length]; |
696 | ||
697 | 4 | for ( int i = 0; i < urls.length; i++ ) |
698 | { | |
699 | // Although this method is deprecated, it is still the most solid way to translate a File to URL | |
700 | //noinspection deprecation | |
701 | 3 | urls[i] = files[i].toURL(); |
702 | } | |
703 | ||
704 | 1 | return urls; |
705 | } | |
706 | ||
707 | /** | |
708 | * Remove extension from filename. | |
709 | * ie | |
710 | * <pre> | |
711 | * foo.txt --> foo | |
712 | * a\b\c.jpg --> a\b\c | |
713 | * a\b\c --> a\b\c | |
714 | * </pre> | |
715 | * | |
716 | * @param filename the path of the file | |
717 | * @return the filename minus extension | |
718 | */ | |
719 | public @Nonnull static String removeExtension( @Nonnull final String filename ) | |
720 | { | |
721 | 0 | String ext = extension( filename ); |
722 | ||
723 | 0 | if ( "".equals( ext ) ) |
724 | { | |
725 | 0 | return filename; |
726 | } | |
727 | ||
728 | 0 | final int index = filename.lastIndexOf( ext ) - 1; |
729 | 0 | return filename.substring( 0, index ); |
730 | } | |
731 | ||
732 | /** | |
733 | * Get extension from filename. | |
734 | * ie | |
735 | * <pre> | |
736 | * foo.txt --> "txt" | |
737 | * a\b\c.jpg --> "jpg" | |
738 | * a\b\c --> "" | |
739 | * </pre> | |
740 | * | |
741 | * @param filename the path of the file | |
742 | * @return the extension of filename or "" if none | |
743 | */ | |
744 | public @Nonnull static String getExtension( @Nonnull final String filename ) | |
745 | { | |
746 | 0 | return extension( filename ); |
747 | } | |
748 | ||
749 | /** | |
750 | * Copy file from source to destination. If <code>destinationDirectory</code> does not exist, it | |
751 | * (and any parent directories) will be created. If a file <code>source</code> in | |
752 | * <code>destinationDirectory</code> exists, it will be overwritten. | |
753 | * | |
754 | * @param source An existing <code>File</code> to copy. | |
755 | * @param destinationDirectory A directory to copy <code>source</code> into. | |
756 | * @throws java.io.FileNotFoundException if <code>source</code> isn't a normal file. | |
757 | * @throws IllegalArgumentException if <code>destinationDirectory</code> isn't a directory. | |
758 | * @throws IOException if <code>source</code> does not exist, the file in | |
759 | * <code>destinationDirectory</code> cannot be written to, or an IO error occurs during copying. | |
760 | */ | |
761 | public static void copyFileToDirectory( @Nonnull final File source, @Nonnull final File destinationDirectory ) | |
762 | throws IOException | |
763 | { | |
764 | 1 | if ( destinationDirectory.exists() && !destinationDirectory.isDirectory() ) |
765 | { | |
766 | 0 | throw new IllegalArgumentException( "Destination is not a directory" ); |
767 | } | |
768 | ||
769 | 1 | copyFile( source, new File( destinationDirectory, source.getName() ) ); |
770 | 1 | } |
771 | ||
772 | /** | |
773 | * Copy file from source to destination only if source is newer than the target file. | |
774 | * If <code>destinationDirectory</code> does not exist, it | |
775 | * (and any parent directories) will be created. If a file <code>source</code> in | |
776 | * <code>destinationDirectory</code> exists, it will be overwritten. | |
777 | * | |
778 | * @param source An existing <code>File</code> to copy. | |
779 | * @param destinationDirectory A directory to copy <code>source</code> into. | |
780 | * @throws java.io.FileNotFoundException if <code>source</code> isn't a normal file. | |
781 | * @throws IllegalArgumentException if <code>destinationDirectory</code> isn't a directory. | |
782 | * @throws IOException if <code>source</code> does not exist, the file in | |
783 | * <code>destinationDirectory</code> cannot be written to, or an IO error occurs during copying. | |
784 | */ | |
785 | private static void copyFileToDirectoryIfModified( @Nonnull final File source, @Nonnull final File destinationDirectory ) | |
786 | throws IOException | |
787 | { | |
788 | 0 | if ( destinationDirectory.exists() && !destinationDirectory.isDirectory() ) |
789 | { | |
790 | 0 | throw new IllegalArgumentException( "Destination is not a directory" ); |
791 | } | |
792 | ||
793 | 0 | copyFileIfModified( source, new File( destinationDirectory, source.getName() ) ); |
794 | 0 | } |
795 | ||
796 | ||
797 | /** | |
798 | * Copy file from source to destination. The directories up to <code>destination</code> will be | |
799 | * created if they don't already exist. <code>destination</code> will be overwritten if it | |
800 | * already exists. | |
801 | * | |
802 | * @param source An existing non-directory <code>File</code> to copy bytes from. | |
803 | * @param destination A non-directory <code>File</code> to write bytes to (possibly | |
804 | * overwriting). | |
805 | * @throws IOException if <code>source</code> does not exist, <code>destination</code> cannot be | |
806 | * written to, or an IO error occurs during copying. | |
807 | * @throws java.io.FileNotFoundException if <code>destination</code> is a directory | |
808 | * | |
809 | */ | |
810 | public static void copyFile( @Nonnull final File source, @Nonnull final File destination ) | |
811 | throws IOException | |
812 | { | |
813 | //check source exists | |
814 | 3 | if ( !source.exists() ) |
815 | { | |
816 | 0 | final String message = "File " + source + " does not exist"; |
817 | 0 | throw new IOException( message ); |
818 | } | |
819 | ||
820 | //check source != destination, see PLXUTILS-10 | |
821 | 3 | if ( source.getCanonicalPath().equals( destination.getCanonicalPath() ) ) |
822 | { | |
823 | //if they are equal, we can exit the method without doing any work | |
824 | 0 | return; |
825 | } | |
826 | ||
827 | 3 | mkdirsFor( destination ); |
828 | ||
829 | 3 | doCopyFile( source, destination ); |
830 | ||
831 | 3 | if ( source.length() != destination.length() ) |
832 | { | |
833 | 0 | final String message = "Failed to copy full contents from " + source + " to " + destination; |
834 | 0 | throw new IOException( message ); |
835 | } | |
836 | 3 | } |
837 | ||
838 | private static void mkdirsFor( @Nonnull File destination ) | |
839 | { | |
840 | //does destination directory exist ? | |
841 | 3 | if ( destination.getParentFile() != null && !destination.getParentFile().exists() ) |
842 | { | |
843 | //noinspection ResultOfMethodCallIgnored | |
844 | 0 | destination.getParentFile().mkdirs(); |
845 | } | |
846 | 3 | } |
847 | ||
848 | private static void doCopyFile( @Nonnull File source, @Nonnull File destination ) | |
849 | throws IOException | |
850 | { | |
851 | 3 | FileInputStream fis = null; |
852 | 3 | FileOutputStream fos = null; |
853 | 3 | FileChannel input = null; |
854 | 3 | FileChannel output = null; |
855 | try | |
856 | { | |
857 | 3 | fis = new FileInputStream( source ); |
858 | 3 | fos = new FileOutputStream( destination ); |
859 | 3 | input = fis.getChannel(); |
860 | 3 | output = fos.getChannel(); |
861 | 3 | long size = input.size(); |
862 | 3 | long pos = 0; |
863 | long count; | |
864 | 3 | while ( pos < size ) |
865 | { | |
866 | 0 | count = size - pos > FILE_COPY_BUFFER_SIZE ? FILE_COPY_BUFFER_SIZE : size - pos; |
867 | 0 | pos += output.transferFrom( input, pos, count ); |
868 | } | |
869 | } | |
870 | finally | |
871 | { | |
872 | 3 | IOUtil.close( output ); |
873 | 3 | IOUtil.close( fos ); |
874 | 3 | IOUtil.close( input ); |
875 | 3 | IOUtil.close( fis ); |
876 | 3 | } |
877 | 3 | } |
878 | ||
879 | /** | |
880 | * Copy file from source to destination only if source timestamp is later than the destination timestamp. | |
881 | * The directories up to <code>destination</code> will be created if they don't already exist. | |
882 | * <code>destination</code> will be overwritten if it already exists. | |
883 | * | |
884 | * @param source An existing non-directory <code>File</code> to copy bytes from. | |
885 | * @param destination A non-directory <code>File</code> to write bytes to (possibly | |
886 | * overwriting). | |
887 | * @return true if no problem occured | |
888 | * @throws IOException if <code>source</code> does not exist, <code>destination</code> cannot be | |
889 | * written to, or an IO error occurs during copying. | |
890 | */ | |
891 | private static boolean copyFileIfModified( @Nonnull final File source, @Nonnull final File destination ) | |
892 | throws IOException | |
893 | { | |
894 | 0 | if ( destination.lastModified() < source.lastModified() ) |
895 | { | |
896 | 0 | copyFile( source, destination ); |
897 | ||
898 | 0 | return true; |
899 | } | |
900 | ||
901 | 0 | return false; |
902 | } | |
903 | ||
904 | /** | |
905 | * Copies bytes from the URL <code>source</code> to a file <code>destination</code>. | |
906 | * The directories up to <code>destination</code> will be created if they don't already exist. | |
907 | * <code>destination</code> will be overwritten if it already exists. | |
908 | * | |
909 | * @param source A <code>URL</code> to copy bytes from. | |
910 | * @param destination A non-directory <code>File</code> to write bytes to (possibly | |
911 | * overwriting). | |
912 | * @throws IOException if | |
913 | * <ul> | |
914 | * <li><code>source</code> URL cannot be opened</li> | |
915 | * <li><code>destination</code> cannot be written to</li> | |
916 | * <li>an IO error occurs during copying</li> | |
917 | * </ul> | |
918 | */ | |
919 | public static void copyURLToFile( @Nonnull final URL source, @Nonnull final File destination ) | |
920 | throws IOException | |
921 | { | |
922 | 4 | copyStreamToFile( source.openStream(), destination ); |
923 | 4 | } |
924 | ||
925 | /** | |
926 | * Copies bytes from the {@link InputStream} <code>source</code> to a file <code>destination</code>. | |
927 | * The directories up to <code>destination</code> will be created if they don't already exist. | |
928 | * <code>destination</code> will be overwritten if it already exists. | |
929 | * | |
930 | * @param source An {@link InputStream} to copy bytes from. This stream is | |
931 | * guaranteed to be closed. | |
932 | * @param destination A non-directory <code>File</code> to write bytes to (possibly | |
933 | * overwriting). | |
934 | * @throws IOException if | |
935 | * <ul> | |
936 | * <li><code>source</code> URL cannot be opened</li> | |
937 | * <li><code>destination</code> cannot be written to</li> | |
938 | * <li>an IO error occurs during copying</li> | |
939 | * </ul> | |
940 | */ | |
941 | private static void copyStreamToFile( @Nonnull final @WillClose InputStream source, @Nonnull final File destination ) | |
942 | throws IOException | |
943 | { | |
944 | 4 | FileOutputStream output = null; |
945 | try | |
946 | { | |
947 | //does destination directory exist ? | |
948 | 4 | if ( destination.getParentFile() != null && !destination.getParentFile().exists() ) |
949 | { | |
950 | //noinspection ResultOfMethodCallIgnored | |
951 | 0 | destination.getParentFile().mkdirs(); |
952 | } | |
953 | ||
954 | //make sure we can write to destination | |
955 | 4 | if ( destination.exists() && !destination.canWrite() ) |
956 | { | |
957 | 0 | final String message = "Unable to open file " + destination + " for writing."; |
958 | 0 | throw new IOException( message ); |
959 | } | |
960 | ||
961 | 4 | output = new FileOutputStream( destination ); |
962 | 4 | IOUtil.copy( source, output ); |
963 | } | |
964 | finally | |
965 | { | |
966 | 4 | IOUtil.close( source ); |
967 | 4 | IOUtil.close( output ); |
968 | 4 | } |
969 | 4 | } |
970 | ||
971 | /** | |
972 | * Normalize a path. | |
973 | * Eliminates "/../" and "/./" in a string. Returns <code>null</code> if the ..'s went past the | |
974 | * root. | |
975 | * Eg: | |
976 | * <pre> | |
977 | * /foo// --> /foo/ | |
978 | * /foo/./ --> /foo/ | |
979 | * /foo/../bar --> /bar | |
980 | * /foo/../bar/ --> /bar/ | |
981 | * /foo/../bar/../baz --> /baz | |
982 | * //foo//./bar --> /foo/bar | |
983 | * /../ --> null | |
984 | * </pre> | |
985 | * | |
986 | * @param path the path to normalize | |
987 | * @return the normalized String, or <code>null</code> if too many ..'s. | |
988 | */ | |
989 | public static String normalize( final String path ) | |
990 | { | |
991 | 0 | String normalized = path; |
992 | // Resolve occurrences of "//" in the normalized path | |
993 | while ( true ) | |
994 | { | |
995 | 0 | int index = normalized.indexOf( "//" ); |
996 | 0 | if ( index < 0 ) |
997 | { | |
998 | 0 | break; |
999 | } | |
1000 | 0 | normalized = normalized.substring( 0, index ) + normalized.substring( index + 1 ); |
1001 | 0 | } |
1002 | ||
1003 | // Resolve occurrences of "/./" in the normalized path | |
1004 | while ( true ) | |
1005 | { | |
1006 | 0 | int index = normalized.indexOf( "/./" ); |
1007 | 0 | if ( index < 0 ) |
1008 | { | |
1009 | 0 | break; |
1010 | } | |
1011 | 0 | normalized = normalized.substring( 0, index ) + normalized.substring( index + 2 ); |
1012 | 0 | } |
1013 | ||
1014 | // Resolve occurrences of "/../" in the normalized path | |
1015 | while ( true ) | |
1016 | { | |
1017 | 0 | int index = normalized.indexOf( "/../" ); |
1018 | 0 | if ( index < 0 ) |
1019 | { | |
1020 | 0 | break; |
1021 | } | |
1022 | 0 | if ( index == 0 ) |
1023 | { | |
1024 | 0 | return null; // Trying to go outside our context |
1025 | } | |
1026 | 0 | int index2 = normalized.lastIndexOf( '/', index - 1 ); |
1027 | 0 | normalized = normalized.substring( 0, index2 ) + normalized.substring( index + 3 ); |
1028 | 0 | } |
1029 | ||
1030 | // Return the normalized path that we have completed | |
1031 | 0 | return normalized; |
1032 | } | |
1033 | ||
1034 | /** | |
1035 | * Resolve a file <code>filename</code> to it's canonical form. If <code>filename</code> is | |
1036 | * relative (doesn't start with <code>/</code>), it will be resolved relative to | |
1037 | * <code>baseFile</code>, otherwise it is treated as a normal root-relative path. | |
1038 | * | |
1039 | * @param baseFile Where to resolve <code>filename</code> from, if <code>filename</code> is | |
1040 | * relative. | |
1041 | * @param filename Absolute or relative file path to resolve. | |
1042 | * @return The canonical <code>File</code> of <code>filename</code>. | |
1043 | */ | |
1044 | public static File resolveFile( final File baseFile, String filename ) | |
1045 | { | |
1046 | 0 | String filenm = filename; |
1047 | 0 | if ( '/' != File.separatorChar ) |
1048 | { | |
1049 | 0 | filenm = filename.replace( '/', File.separatorChar ); |
1050 | } | |
1051 | ||
1052 | 0 | if ( '\\' != File.separatorChar ) |
1053 | { | |
1054 | 0 | filenm = filename.replace( '\\', File.separatorChar ); |
1055 | } | |
1056 | ||
1057 | // deal with absolute files | |
1058 | 0 | if ( filenm.startsWith( File.separator ) || ( Os.isFamily( Os.FAMILY_WINDOWS ) && filenm.indexOf( ":" ) > 0 ) ) |
1059 | { | |
1060 | 0 | File file = new File( filenm ); |
1061 | ||
1062 | try | |
1063 | { | |
1064 | 0 | file = file.getCanonicalFile(); |
1065 | } | |
1066 | 0 | catch ( final IOException ioe ) |
1067 | { | |
1068 | // nop | |
1069 | 0 | } |
1070 | ||
1071 | 0 | return file; |
1072 | } | |
1073 | // FIXME: I'm almost certain this // removal is unnecessary, as getAbsoluteFile() strips | |
1074 | // them. However, I'm not sure about this UNC stuff. (JT) | |
1075 | 0 | final char[] chars = filename.toCharArray(); |
1076 | 0 | final StringBuilder sb = new StringBuilder(); |
1077 | ||
1078 | //remove duplicate file separators in succession - except | |
1079 | //on win32 at start of filename as UNC filenames can | |
1080 | //be \\AComputer\AShare\myfile.txt | |
1081 | 0 | int start = 0; |
1082 | 0 | if ( '\\' == File.separatorChar ) |
1083 | { | |
1084 | 0 | sb.append( filenm.charAt( 0 ) ); |
1085 | 0 | start++; |
1086 | } | |
1087 | ||
1088 | 0 | for ( int i = start; i < chars.length; i++ ) |
1089 | { | |
1090 | 0 | final boolean doubleSeparator = File.separatorChar == chars[i] && File.separatorChar == chars[i - 1]; |
1091 | ||
1092 | 0 | if ( !doubleSeparator ) |
1093 | { | |
1094 | 0 | sb.append( chars[i] ); |
1095 | } | |
1096 | } | |
1097 | ||
1098 | 0 | filenm = sb.toString(); |
1099 | ||
1100 | //must be relative | |
1101 | 0 | File file = ( new File( baseFile, filenm ) ).getAbsoluteFile(); |
1102 | ||
1103 | try | |
1104 | { | |
1105 | 0 | file = file.getCanonicalFile(); |
1106 | } | |
1107 | 0 | catch ( final IOException ioe ) |
1108 | { | |
1109 | // nop | |
1110 | 0 | } |
1111 | ||
1112 | 0 | return file; |
1113 | } | |
1114 | ||
1115 | /** | |
1116 | * Delete a file. If file is directory delete it and all sub-directories. | |
1117 | * | |
1118 | * @param file the file path | |
1119 | * @throws IOException if any | |
1120 | */ | |
1121 | public static void forceDelete( final String file ) | |
1122 | throws IOException | |
1123 | { | |
1124 | 0 | forceDelete( new File( file ) ); |
1125 | 0 | } |
1126 | ||
1127 | /** | |
1128 | * Delete a file. If file is directory delete it and all sub-directories. | |
1129 | * | |
1130 | * @param file a file | |
1131 | * @throws IOException if any | |
1132 | */ | |
1133 | public static void forceDelete( @Nonnull final File file ) | |
1134 | throws IOException | |
1135 | { | |
1136 | 156 | if ( file.isDirectory() ) |
1137 | { | |
1138 | 2 | deleteDirectory( file ); |
1139 | } | |
1140 | else | |
1141 | { | |
1142 | /* | |
1143 | * NOTE: Always try to delete the file even if it appears to be non-existent. This will ensure that a | |
1144 | * symlink whose target does not exist is deleted, too. | |
1145 | */ | |
1146 | 154 | boolean filePresent = file.getCanonicalFile().exists(); |
1147 | 154 | if ( !deleteFile( file ) && filePresent ) |
1148 | { | |
1149 | 0 | final String message = "File " + file + " unable to be deleted."; |
1150 | 0 | throw new IOException( message ); |
1151 | } | |
1152 | } | |
1153 | 156 | } |
1154 | ||
1155 | /** | |
1156 | * Accommodate Windows bug encountered in both Sun and IBM JDKs. | |
1157 | * Others possible. If the delete does not work, call System.gc(), | |
1158 | * wait a little and try again. | |
1159 | * | |
1160 | * @param file a file | |
1161 | * @throws IOException if any | |
1162 | */ | |
1163 | private static boolean deleteFile( @Nonnull File file ) | |
1164 | throws IOException | |
1165 | { | |
1166 | 154 | if ( file.isDirectory() ) |
1167 | { | |
1168 | 0 | throw new IOException( "File " + file + " isn't a file." ); |
1169 | } | |
1170 | ||
1171 | 154 | if ( !file.delete() ) |
1172 | { | |
1173 | 0 | if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) |
1174 | { | |
1175 | 0 | file = file.getCanonicalFile(); |
1176 | 0 | System.gc(); |
1177 | } | |
1178 | ||
1179 | try | |
1180 | { | |
1181 | 0 | Thread.sleep( 10 ); |
1182 | 0 | return file.delete(); |
1183 | } | |
1184 | 0 | catch ( InterruptedException ex ) |
1185 | { | |
1186 | 0 | return file.delete(); |
1187 | } | |
1188 | } | |
1189 | ||
1190 | 154 | return true; |
1191 | } | |
1192 | ||
1193 | ||
1194 | /** | |
1195 | * Make a directory. | |
1196 | * | |
1197 | * @param file not null | |
1198 | * @throws IOException If there already exists a file with specified name or | |
1199 | * the directory is unable to be created | |
1200 | * @throws IllegalArgumentException if the file contains illegal Windows characters under Windows OS. | |
1201 | * @see #INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME | |
1202 | */ | |
1203 | public static void forceMkdir( @Nonnull final File file ) | |
1204 | throws IOException | |
1205 | { | |
1206 | 3 | if ( Os.isFamily( Os.FAMILY_WINDOWS ) && !isValidWindowsFileName( file ) ) |
1207 | { | |
1208 | 0 | throw new IllegalArgumentException( |
1209 | "The file (" + file.getAbsolutePath() + ") cannot contain any of the following characters: \n" | |
1210 | + StringUtils.join( INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME, " " ) ); | |
1211 | } | |
1212 | ||
1213 | 3 | if ( file.exists() ) |
1214 | { | |
1215 | 2 | if ( file.isFile() ) |
1216 | { | |
1217 | 1 | final String message = |
1218 | "File " + file + " exists and is " + "not a directory. Unable to create directory."; | |
1219 | 1 | throw new IOException( message ); |
1220 | } | |
1221 | } | |
1222 | else | |
1223 | { | |
1224 | 1 | if ( !file.mkdirs() ) |
1225 | { | |
1226 | 0 | final String message = "Unable to create directory " + file; |
1227 | 0 | throw new IOException( message ); |
1228 | } | |
1229 | } | |
1230 | 2 | } |
1231 | ||
1232 | /** | |
1233 | * Recursively delete a directory. | |
1234 | * | |
1235 | * @param directory a directory | |
1236 | * @throws IOException if any | |
1237 | */ | |
1238 | public static void deleteDirectory( @Nonnull final String directory ) | |
1239 | throws IOException | |
1240 | { | |
1241 | 0 | deleteDirectory( new File( directory ) ); |
1242 | 0 | } |
1243 | ||
1244 | /** | |
1245 | * Recursively delete a directory. | |
1246 | * | |
1247 | * @param directory a directory | |
1248 | * @throws IOException if any | |
1249 | */ | |
1250 | public static void deleteDirectory( @Nonnull final File directory ) | |
1251 | throws IOException | |
1252 | { | |
1253 | 79 | if ( !directory.exists() ) |
1254 | { | |
1255 | 1 | return; |
1256 | } | |
1257 | ||
1258 | /* try delete the directory before its contents, which will take | |
1259 | * care of any directories that are really symbolic links. | |
1260 | */ | |
1261 | 77 | if ( directory.delete() ) |
1262 | { | |
1263 | 2 | return; |
1264 | } | |
1265 | ||
1266 | 75 | cleanDirectory( directory ); |
1267 | 75 | if ( !directory.delete() ) |
1268 | { | |
1269 | 0 | final String message = "Directory " + directory + " unable to be deleted."; |
1270 | 0 | throw new IOException( message ); |
1271 | } | |
1272 | 75 | } |
1273 | ||
1274 | /** | |
1275 | * Clean a directory without deleting it. | |
1276 | * | |
1277 | * @param directory a directory | |
1278 | * @throws IOException if any | |
1279 | */ | |
1280 | public static void cleanDirectory( @Nonnull final File directory ) | |
1281 | throws IOException | |
1282 | { | |
1283 | 75 | if ( !directory.exists() ) |
1284 | { | |
1285 | 0 | final String message = directory + " does not exist"; |
1286 | 0 | throw new IllegalArgumentException( message ); |
1287 | } | |
1288 | ||
1289 | 75 | if ( !directory.isDirectory() ) |
1290 | { | |
1291 | 0 | final String message = directory + " is not a directory"; |
1292 | 0 | throw new IllegalArgumentException( message ); |
1293 | } | |
1294 | ||
1295 | 75 | IOException exception = null; |
1296 | ||
1297 | 75 | final File[] files = directory.listFiles(); |
1298 | ||
1299 | 75 | if ( files == null ) |
1300 | { | |
1301 | 0 | return; |
1302 | } | |
1303 | ||
1304 | 224 | for ( final File file : files ) |
1305 | { | |
1306 | try | |
1307 | { | |
1308 | 149 | forceDelete( file ); |
1309 | } | |
1310 | 0 | catch ( final IOException ioe ) |
1311 | { | |
1312 | 0 | exception = ioe; |
1313 | 149 | } |
1314 | } | |
1315 | ||
1316 | 75 | if ( null != exception ) |
1317 | { | |
1318 | 0 | throw exception; |
1319 | } | |
1320 | 75 | } |
1321 | ||
1322 | /** | |
1323 | * Recursively count size of a directory. | |
1324 | * | |
1325 | * @param directory a directory | |
1326 | * @return size of directory in bytes. | |
1327 | */ | |
1328 | public static long sizeOfDirectory( @Nonnull final String directory ) | |
1329 | { | |
1330 | 0 | return sizeOfDirectory( new File( directory ) ); |
1331 | } | |
1332 | ||
1333 | /** | |
1334 | * Recursively count size of a directory. | |
1335 | * | |
1336 | * @param directory a directory | |
1337 | * @return size of directory in bytes. | |
1338 | */ | |
1339 | public static long sizeOfDirectory( @Nonnull final File directory ) | |
1340 | { | |
1341 | 3 | if ( !directory.exists() ) |
1342 | { | |
1343 | 1 | final String message = directory + " does not exist"; |
1344 | 1 | throw new IllegalArgumentException( message ); |
1345 | } | |
1346 | ||
1347 | 2 | if ( !directory.isDirectory() ) |
1348 | { | |
1349 | 1 | final String message = directory + " is not a directory"; |
1350 | 1 | throw new IllegalArgumentException( message ); |
1351 | } | |
1352 | ||
1353 | 1 | long size = 0; |
1354 | ||
1355 | 1 | final File[] files = directory.listFiles(); |
1356 | 1 | if ( files == null ) |
1357 | { | |
1358 | 0 | throw new IllegalArgumentException( "Problems reading directory" ); |
1359 | } | |
1360 | ||
1361 | 1 | for ( final File file : files ) |
1362 | { | |
1363 | 0 | if ( file.isDirectory() ) |
1364 | { | |
1365 | 0 | size += sizeOfDirectory( file ); |
1366 | } | |
1367 | else | |
1368 | { | |
1369 | 0 | size += file.length(); |
1370 | } | |
1371 | } | |
1372 | ||
1373 | 1 | return size; |
1374 | } | |
1375 | ||
1376 | /** | |
1377 | * Return the files contained in the directory, using inclusion and exclusion Ant patterns, | |
1378 | * including the directory name in each of the files | |
1379 | * | |
1380 | * @param directory the directory to scan | |
1381 | * @param includes the includes pattern, comma separated | |
1382 | * @param excludes the excludes pattern, comma separated | |
1383 | * @return a list of File objects | |
1384 | * @throws IOException | |
1385 | * @see #getFileNames(File, String, String, boolean) | |
1386 | */ | |
1387 | public @Nonnull static List<File> getFiles( @Nonnull File directory, @Nullable String includes, @Nullable String excludes ) | |
1388 | throws IOException | |
1389 | { | |
1390 | 1 | return getFiles( directory, includes, excludes, true ); |
1391 | } | |
1392 | ||
1393 | /** | |
1394 | * Return the files contained in the directory, using inclusion and exclusion Ant patterns | |
1395 | * | |
1396 | * @param directory the directory to scan | |
1397 | * @param includes the includes pattern, comma separated | |
1398 | * @param excludes the excludes pattern, comma separated | |
1399 | * @param includeBasedir true to include the base dir in each file | |
1400 | * @return a list of File objects | |
1401 | * @throws IOException | |
1402 | * @see #getFileNames(File, String, String, boolean) | |
1403 | */ | |
1404 | public @Nonnull static List<File> getFiles( @Nonnull File directory, @Nullable String includes, @Nullable String excludes, boolean includeBasedir ) | |
1405 | throws IOException | |
1406 | { | |
1407 | 1 | List<String> fileNames = getFileNames( directory, includes, excludes, includeBasedir ); |
1408 | ||
1409 | 1 | List<File> files = new ArrayList<File>(); |
1410 | ||
1411 | 1 | for ( String filename : fileNames ) |
1412 | { | |
1413 | 0 | files.add( new File( filename ) ); |
1414 | } | |
1415 | ||
1416 | 1 | return files; |
1417 | } | |
1418 | ||
1419 | /** | |
1420 | * Return a list of files as String depending options. | |
1421 | * This method use case sensitive file name. | |
1422 | * | |
1423 | * @param directory the directory to scan | |
1424 | * @param includes the includes pattern, comma separated | |
1425 | * @param excludes the excludes pattern, comma separated | |
1426 | * @param includeBasedir true to include the base dir in each String of file | |
1427 | * @return a list of files as String | |
1428 | * @throws IOException | |
1429 | */ | |
1430 | public @Nonnull static List<String> getFileNames( @Nonnull File directory, @Nullable String includes, @Nullable String excludes, boolean includeBasedir ) | |
1431 | throws IOException | |
1432 | { | |
1433 | 1 | return getFileNames( directory, includes, excludes, includeBasedir, true ); |
1434 | } | |
1435 | ||
1436 | /** | |
1437 | * Return a list of files as String depending options. | |
1438 | * | |
1439 | * @param directory the directory to scan | |
1440 | * @param includes the includes pattern, comma separated | |
1441 | * @param excludes the excludes pattern, comma separated | |
1442 | * @param includeBasedir true to include the base dir in each String of file | |
1443 | * @param isCaseSensitive true if case sensitive | |
1444 | * @return a list of files as String | |
1445 | * @throws IOException | |
1446 | */ | |
1447 | private @Nonnull static List<String> getFileNames( @Nonnull File directory, @Nullable String includes, @Nullable String excludes, boolean includeBasedir, | |
1448 | boolean isCaseSensitive ) | |
1449 | throws IOException | |
1450 | { | |
1451 | 1 | return getFileAndDirectoryNames( directory, includes, excludes, includeBasedir, isCaseSensitive, true, false ); |
1452 | } | |
1453 | ||
1454 | /** | |
1455 | * Return a list of directories as String depending options. | |
1456 | * This method use case sensitive file name. | |
1457 | * | |
1458 | * @param directory the directory to scan | |
1459 | * @param includes the includes pattern, comma separated | |
1460 | * @param excludes the excludes pattern, comma separated | |
1461 | * @param includeBasedir true to include the base dir in each String of file | |
1462 | * @return a list of directories as String | |
1463 | * @throws IOException | |
1464 | */ | |
1465 | public @Nonnull static List<String> getDirectoryNames( @Nonnull File directory, @Nullable String includes, @Nullable String excludes, | |
1466 | boolean includeBasedir ) | |
1467 | throws IOException | |
1468 | { | |
1469 | 0 | return getDirectoryNames( directory, includes, excludes, includeBasedir, true ); |
1470 | } | |
1471 | ||
1472 | /** | |
1473 | * Return a list of directories as String depending options. | |
1474 | * | |
1475 | * @param directory the directory to scan | |
1476 | * @param includes the includes pattern, comma separated | |
1477 | * @param excludes the excludes pattern, comma separated | |
1478 | * @param includeBasedir true to include the base dir in each String of file | |
1479 | * @param isCaseSensitive true if case sensitive | |
1480 | * @return a list of directories as String | |
1481 | * @throws IOException | |
1482 | */ | |
1483 | public @Nonnull static List<String> getDirectoryNames( @Nonnull File directory, @Nullable String includes, @Nullable String excludes, | |
1484 | boolean includeBasedir, boolean isCaseSensitive ) | |
1485 | throws IOException | |
1486 | { | |
1487 | 0 | return getFileAndDirectoryNames( directory, includes, excludes, includeBasedir, isCaseSensitive, false, true ); |
1488 | } | |
1489 | ||
1490 | /** | |
1491 | * Return a list of files as String depending options. | |
1492 | * | |
1493 | * @param directory the directory to scan | |
1494 | * @param includes the includes pattern, comma separated | |
1495 | * @param excludes the excludes pattern, comma separated | |
1496 | * @param includeBasedir true to include the base dir in each String of file | |
1497 | * @param isCaseSensitive true if case sensitive | |
1498 | * @param getFiles true if get files | |
1499 | * @param getDirectories true if get directories | |
1500 | * @return a list of files as String | |
1501 | */ | |
1502 | public @Nonnull static List<String> getFileAndDirectoryNames( File directory, @Nullable String includes, @Nullable String excludes, | |
1503 | boolean includeBasedir, boolean isCaseSensitive, | |
1504 | boolean getFiles, boolean getDirectories ) | |
1505 | { | |
1506 | 2 | DirectoryScanner scanner = new DirectoryScanner(); |
1507 | ||
1508 | 2 | scanner.setBasedir( directory ); |
1509 | ||
1510 | 2 | if ( includes != null ) |
1511 | { | |
1512 | 1 | scanner.setIncludes( StringUtils.split( includes, "," ) ); |
1513 | } | |
1514 | ||
1515 | 2 | if ( excludes != null ) |
1516 | { | |
1517 | 0 | scanner.setExcludes( StringUtils.split( excludes, "," ) ); |
1518 | } | |
1519 | ||
1520 | 2 | scanner.setCaseSensitive( isCaseSensitive ); |
1521 | ||
1522 | 2 | scanner.scan(); |
1523 | ||
1524 | 2 | List<String> list = new ArrayList<String>(); |
1525 | ||
1526 | 2 | if ( getFiles ) |
1527 | { | |
1528 | 2 | String[] files = scanner.getIncludedFiles(); |
1529 | ||
1530 | 2 | for ( String file : files ) |
1531 | { | |
1532 | 0 | if ( includeBasedir ) |
1533 | { | |
1534 | 0 | list.add( directory + FileUtils.FS + file ); |
1535 | } | |
1536 | else | |
1537 | { | |
1538 | 0 | list.add( file ); |
1539 | } | |
1540 | } | |
1541 | } | |
1542 | ||
1543 | 2 | if ( getDirectories ) |
1544 | { | |
1545 | 1 | String[] directories = scanner.getIncludedDirectories(); |
1546 | ||
1547 | 2 | for ( String directory1 : directories ) |
1548 | { | |
1549 | 1 | if ( includeBasedir ) |
1550 | { | |
1551 | 1 | list.add( directory + FileUtils.FS + directory1 ); |
1552 | } | |
1553 | else | |
1554 | { | |
1555 | 0 | list.add( directory1 ); |
1556 | } | |
1557 | } | |
1558 | } | |
1559 | ||
1560 | 2 | return list; |
1561 | } | |
1562 | ||
1563 | /** | |
1564 | * Copy a directory to an other one. | |
1565 | * | |
1566 | * @param sourceDirectory the source dir | |
1567 | * @param destinationDirectory the target dir | |
1568 | * @throws IOException if any | |
1569 | */ | |
1570 | public static void copyDirectory( File sourceDirectory, File destinationDirectory ) | |
1571 | throws IOException | |
1572 | { | |
1573 | 1 | copyDirectory( sourceDirectory, destinationDirectory, "**", null ); |
1574 | 1 | } |
1575 | ||
1576 | /** | |
1577 | * Copy a directory to an other one. | |
1578 | * | |
1579 | * @param sourceDirectory the source dir | |
1580 | * @param destinationDirectory the target dir | |
1581 | * @param includes include pattern | |
1582 | * @param excludes exlucde pattern | |
1583 | * @throws IOException if any | |
1584 | * @see #getFiles(File, String, String) | |
1585 | */ | |
1586 | public static void copyDirectory( @Nonnull File sourceDirectory, @Nonnull File destinationDirectory, @Nullable String includes, | |
1587 | @Nullable String excludes ) | |
1588 | throws IOException | |
1589 | { | |
1590 | 1 | if ( !sourceDirectory.exists() ) |
1591 | { | |
1592 | 0 | return; |
1593 | } | |
1594 | ||
1595 | 1 | List<File> files = getFiles( sourceDirectory, includes, excludes ); |
1596 | ||
1597 | 1 | for ( File file : files ) |
1598 | { | |
1599 | 0 | copyFileToDirectory( file, destinationDirectory ); |
1600 | } | |
1601 | 1 | } |
1602 | ||
1603 | /** | |
1604 | * Copies a entire directory structure. | |
1605 | * <p/> | |
1606 | * Note: | |
1607 | * <ul> | |
1608 | * <li>It will include empty directories. | |
1609 | * <li>The <code>sourceDirectory</code> must exists. | |
1610 | * </ul> | |
1611 | * | |
1612 | * @param sourceDirectory the source dir | |
1613 | * @param destinationDirectory the target dir | |
1614 | * @throws IOException if any | |
1615 | */ | |
1616 | public static void copyDirectoryStructure( @Nonnull File sourceDirectory, @Nonnull File destinationDirectory ) | |
1617 | throws IOException | |
1618 | { | |
1619 | 0 | copyDirectoryStructure( sourceDirectory, destinationDirectory, destinationDirectory, false ); |
1620 | 0 | } |
1621 | ||
1622 | private static void copyDirectoryStructure( @Nonnull File sourceDirectory, @Nonnull File destinationDirectory, | |
1623 | File rootDestinationDirectory, boolean onlyModifiedFiles ) | |
1624 | throws IOException | |
1625 | { | |
1626 | //noinspection ConstantConditions | |
1627 | 0 | if ( sourceDirectory == null ) |
1628 | { | |
1629 | 0 | throw new IOException( "source directory can't be null." ); |
1630 | } | |
1631 | ||
1632 | //noinspection ConstantConditions | |
1633 | 0 | if ( destinationDirectory == null ) |
1634 | { | |
1635 | 0 | throw new IOException( "destination directory can't be null." ); |
1636 | } | |
1637 | ||
1638 | 0 | if ( sourceDirectory.equals( destinationDirectory ) ) |
1639 | { | |
1640 | 0 | throw new IOException( "source and destination are the same directory." ); |
1641 | } | |
1642 | ||
1643 | 0 | if ( !sourceDirectory.exists() ) |
1644 | { | |
1645 | 0 | throw new IOException( "Source directory doesn't exists (" + sourceDirectory.getAbsolutePath() + ")." ); |
1646 | } | |
1647 | ||
1648 | 0 | File[] files = sourceDirectory.listFiles(); |
1649 | ||
1650 | 0 | if ( files == null ) |
1651 | { | |
1652 | 0 | return; |
1653 | } | |
1654 | ||
1655 | 0 | String sourcePath = sourceDirectory.getAbsolutePath(); |
1656 | ||
1657 | 0 | for ( File file : files ) |
1658 | { | |
1659 | 0 | if ( file.equals( rootDestinationDirectory ) ) |
1660 | { | |
1661 | // We don't copy the destination directory in itself | |
1662 | 0 | continue; |
1663 | } | |
1664 | ||
1665 | 0 | String dest = file.getAbsolutePath(); |
1666 | ||
1667 | 0 | dest = dest.substring( sourcePath.length() + 1 ); |
1668 | ||
1669 | 0 | File destination = new File( destinationDirectory, dest ); |
1670 | ||
1671 | 0 | if ( file.isFile() ) |
1672 | { | |
1673 | 0 | destination = destination.getParentFile(); |
1674 | ||
1675 | 0 | if ( onlyModifiedFiles ) |
1676 | { | |
1677 | 0 | copyFileToDirectoryIfModified( file, destination ); |
1678 | } | |
1679 | else | |
1680 | { | |
1681 | 0 | copyFileToDirectory( file, destination ); |
1682 | } | |
1683 | } | |
1684 | 0 | else if ( file.isDirectory() ) |
1685 | { | |
1686 | 0 | if ( !destination.exists() && !destination.mkdirs() ) |
1687 | { | |
1688 | 0 | throw new IOException( "Could not create destination directory '" + destination.getAbsolutePath() |
1689 | + "'." ); | |
1690 | } | |
1691 | ||
1692 | 0 | copyDirectoryStructure( file, destination, rootDestinationDirectory, onlyModifiedFiles ); |
1693 | } | |
1694 | else | |
1695 | { | |
1696 | 0 | throw new IOException( "Unknown file type: " + file.getAbsolutePath() ); |
1697 | } | |
1698 | } | |
1699 | 0 | } |
1700 | ||
1701 | /** | |
1702 | * Renames a file, even if that involves crossing file system boundaries. | |
1703 | * <p/> | |
1704 | * <p>This will remove <code>to</code> (if it exists), ensure that | |
1705 | * <code>to</code>'s parent directory exists and move | |
1706 | * <code>from</code>, which involves deleting <code>from</code> as | |
1707 | * well.</p> | |
1708 | * | |
1709 | * @param from the file to move | |
1710 | * @param to the new file name | |
1711 | * @throws IOException if anything bad happens during this process. | |
1712 | * Note that <code>to</code> may have been deleted already when this happens. | |
1713 | */ | |
1714 | public static void rename( @Nonnull File from, @Nonnull File to ) | |
1715 | throws IOException | |
1716 | { | |
1717 | 0 | if ( to.exists() && !to.delete() ) |
1718 | { | |
1719 | 0 | throw new IOException( "Failed to delete " + to + " while trying to rename " + from ); |
1720 | } | |
1721 | ||
1722 | 0 | File parent = to.getParentFile(); |
1723 | 0 | if ( parent != null && !parent.exists() && !parent.mkdirs() ) |
1724 | { | |
1725 | 0 | throw new IOException( "Failed to create directory " + parent + " while trying to rename " + from ); |
1726 | } | |
1727 | ||
1728 | 0 | if ( !from.renameTo( to ) ) |
1729 | { | |
1730 | 0 | copyFile( from, to ); |
1731 | 0 | if ( !from.delete() ) |
1732 | { | |
1733 | 0 | throw new IOException( "Failed to delete " + from + " while trying to rename it." ); |
1734 | } | |
1735 | } | |
1736 | 0 | } |
1737 | ||
1738 | /** | |
1739 | * Create a temporary file in a given directory. | |
1740 | * <p/> | |
1741 | * <p>The file denoted by the returned abstract pathname did not | |
1742 | * exist before this method was invoked, any subsequent invocation | |
1743 | * of this method will yield a different file name.</p> | |
1744 | * <p/> | |
1745 | * The filename is prefixNNNNNsuffix where NNNN is a random number | |
1746 | * </p> | |
1747 | * <p>This method is different to {@link File#createTempFile(String, String, File)} of JDK 1.2 | |
1748 | * as it doesn't create the file itself. | |
1749 | * It uses the location pointed to by java.io.tmpdir | |
1750 | * when the parentDir attribute is | |
1751 | * null.</p> | |
1752 | * <p>To delete automatically the file created by this method, use the | |
1753 | * {@link File#deleteOnExit()} method.</p> | |
1754 | * | |
1755 | * @param prefix prefix before the random number | |
1756 | * @param suffix file extension; include the '.' | |
1757 | * @param parentDir Directory to create the temporary file in <code>-java.io.tmpdir</code> | |
1758 | * used if not specificed | |
1759 | * @return a File reference to the new temporary file. | |
1760 | */ | |
1761 | public static File createTempFile( @Nonnull String prefix, @Nonnull String suffix, @Nullable File parentDir ) | |
1762 | { | |
1763 | File result; | |
1764 | 0 | String parent = System.getProperty( "java.io.tmpdir" ); |
1765 | 0 | if ( parentDir != null ) |
1766 | { | |
1767 | 0 | parent = parentDir.getPath(); |
1768 | } | |
1769 | 0 | DecimalFormat fmt = new DecimalFormat( "#####" ); |
1770 | 0 | SecureRandom secureRandom = new SecureRandom(); |
1771 | 0 | long secureInitializer = secureRandom.nextLong(); |
1772 | 0 | Random rand = new Random( secureInitializer + Runtime.getRuntime().freeMemory() ); |
1773 | do | |
1774 | { | |
1775 | 0 | result = new File( parent, prefix + fmt.format( positiveRandom( rand ) ) + suffix ); |
1776 | } | |
1777 | 0 | while ( result.exists() ); |
1778 | ||
1779 | 0 | return result; |
1780 | } | |
1781 | ||
1782 | private static int positiveRandom( Random rand ) | |
1783 | { | |
1784 | 0 | int a = rand.nextInt(); |
1785 | 0 | while ( a == Integer.MIN_VALUE ) |
1786 | { | |
1787 | 0 | a = rand.nextInt(); |
1788 | } | |
1789 | 0 | return Math.abs( a ); |
1790 | } | |
1791 | ||
1792 | /** | |
1793 | * <b>If wrappers is null or empty, the file will be copy only if to.lastModified() < from.lastModified()</b> | |
1794 | * | |
1795 | * @param from the file to copy | |
1796 | * @param to the destination file | |
1797 | * @param encoding the file output encoding (only if wrappers is not empty) | |
1798 | * @param wrappers array of {@link FilterWrapper} | |
1799 | * @throws IOException if an IO error occurs during copying or filtering | |
1800 | */ | |
1801 | public static void copyFile( @Nonnull File from, @Nonnull File to, @Nullable String encoding, @Nullable FilterWrapper... wrappers ) | |
1802 | throws IOException | |
1803 | { | |
1804 | 0 | copyFile( from, to, encoding, wrappers, false ); |
1805 | 0 | } |
1806 | ||
1807 | 0 | public static abstract class FilterWrapper |
1808 | { | |
1809 | public abstract Reader getReader( Reader fileReader ); | |
1810 | } | |
1811 | ||
1812 | /** | |
1813 | * <b>If wrappers is null or empty, the file will be copy only if to.lastModified() < from.lastModified() or if overwrite is true</b> | |
1814 | * | |
1815 | * @param from the file to copy | |
1816 | * @param to the destination file | |
1817 | * @param encoding the file output encoding (only if wrappers is not empty) | |
1818 | * @param wrappers array of {@link FilterWrapper} | |
1819 | * @param overwrite if true and f wrappers is null or empty, the file will be copy | |
1820 | * enven if to.lastModified() < from.lastModified() | |
1821 | * @throws IOException if an IO error occurs during copying or filtering | |
1822 | * | |
1823 | */ | |
1824 | public static void copyFile( @Nonnull File from, @Nonnull File to, @Nullable String encoding, @Nullable FilterWrapper[] wrappers, boolean overwrite ) | |
1825 | throws IOException | |
1826 | { | |
1827 | 0 | if ( wrappers != null && wrappers.length > 0 ) |
1828 | { | |
1829 | // buffer so it isn't reading a byte at a time! | |
1830 | 0 | Reader fileReader = null; |
1831 | 0 | Writer fileWriter = null; |
1832 | try | |
1833 | { | |
1834 | 0 | if ( encoding == null || encoding.length() < 1 ) |
1835 | { | |
1836 | 0 | fileReader = new BufferedReader( new FileReader( from ) ); |
1837 | 0 | fileWriter = new FileWriter( to ); |
1838 | } | |
1839 | else | |
1840 | { | |
1841 | 0 | FileInputStream instream = new FileInputStream( from ); |
1842 | ||
1843 | 0 | FileOutputStream outstream = new FileOutputStream( to ); |
1844 | ||
1845 | 0 | fileReader = new BufferedReader( new InputStreamReader( instream, encoding ) ); |
1846 | ||
1847 | 0 | fileWriter = new OutputStreamWriter( outstream, encoding ); |
1848 | } | |
1849 | ||
1850 | 0 | Reader reader = fileReader; |
1851 | 0 | for ( FilterWrapper wrapper : wrappers ) |
1852 | { | |
1853 | 0 | reader = wrapper.getReader( reader ); |
1854 | } | |
1855 | ||
1856 | 0 | IOUtil.copy( reader, fileWriter ); |
1857 | } | |
1858 | finally | |
1859 | { | |
1860 | 0 | IOUtil.close( fileReader ); |
1861 | 0 | IOUtil.close( fileWriter ); |
1862 | 0 | } |
1863 | 0 | } |
1864 | else | |
1865 | { | |
1866 | 0 | if ( to.lastModified() < from.lastModified() || overwrite ) |
1867 | { | |
1868 | 0 | copyFile( from, to ); |
1869 | } | |
1870 | } | |
1871 | 0 | } |
1872 | ||
1873 | /** | |
1874 | * Note: the file content is read with platform encoding | |
1875 | * | |
1876 | * @param file the file | |
1877 | * @return a List containing every every line not starting with # and not empty | |
1878 | * @throws IOException if any | |
1879 | */ | |
1880 | public @Nonnull static List<String> loadFile( @Nonnull File file ) | |
1881 | throws IOException | |
1882 | { | |
1883 | 0 | List<String> lines = new ArrayList<String>(); |
1884 | ||
1885 | 0 | if ( file.exists() ) |
1886 | { | |
1887 | 0 | FileReader fileReader = new FileReader( file ); |
1888 | try | |
1889 | { | |
1890 | 0 | BufferedReader reader = new BufferedReader( fileReader ); |
1891 | ||
1892 | 0 | String line = reader.readLine(); |
1893 | ||
1894 | 0 | while ( line != null ) |
1895 | { | |
1896 | 0 | line = line.trim(); |
1897 | ||
1898 | 0 | if ( !line.startsWith( "#" ) && line.length() != 0 ) |
1899 | { | |
1900 | 0 | lines.add( line ); |
1901 | } | |
1902 | 0 | line = reader.readLine(); |
1903 | } | |
1904 | ||
1905 | 0 | reader.close(); |
1906 | } | |
1907 | finally | |
1908 | { | |
1909 | 0 | fileReader.close(); |
1910 | 0 | } |
1911 | } | |
1912 | ||
1913 | 0 | return lines; |
1914 | } | |
1915 | ||
1916 | /** | |
1917 | * For Windows OS, check if the file name contains any of the following characters: | |
1918 | * <code>":", "*", "?", "\"", "<", ">", "|"</code> | |
1919 | * | |
1920 | * @param f not null file | |
1921 | * @return <code>false</code> if the file path contains any of forbidden Windows characters, | |
1922 | * <code>true</code> if the Os is not Windows or if the file path respect the Windows constraints. | |
1923 | * @see #INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME | |
1924 | * | |
1925 | */ | |
1926 | private static boolean isValidWindowsFileName( @Nonnull File f ) | |
1927 | { | |
1928 | 0 | if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) |
1929 | { | |
1930 | 0 | if ( StringUtils.indexOfAny( f.getName(), INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME ) != -1 ) |
1931 | { | |
1932 | 0 | return false; |
1933 | } | |
1934 | ||
1935 | 0 | if ( f.getParentFile() != null ) |
1936 | { | |
1937 | 0 | return isValidWindowsFileName( f.getParentFile() ); |
1938 | } | |
1939 | } | |
1940 | ||
1941 | 0 | return true; |
1942 | } | |
1943 | } |