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