View Javadoc
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.maven.shared.utils.Os;
23  import org.apache.maven.shared.utils.StringUtils;
24  
25  import javax.annotation.Nonnull;
26  import javax.annotation.Nullable;
27  import javax.annotation.WillClose;
28  
29  import java.io.BufferedReader;
30  import java.io.File;
31  import java.io.FileInputStream;
32  import java.io.FileOutputStream;
33  import java.io.IOException;
34  import java.io.InputStream;
35  import java.io.OutputStream;
36  import java.io.RandomAccessFile;
37  import java.io.Reader;
38  import java.io.Writer;
39  import java.net.URL;
40  import java.nio.Buffer;
41  import java.nio.ByteBuffer;
42  import java.nio.CharBuffer;
43  import java.nio.channels.FileChannel;
44  import java.nio.charset.Charset;
45  import java.nio.charset.CharsetEncoder;
46  import java.nio.charset.CoderResult;
47  import java.nio.file.Files;
48  import java.nio.file.Path;
49  import java.security.SecureRandom;
50  import java.text.DecimalFormat;
51  import java.util.ArrayList;
52  import java.util.Arrays;
53  import java.util.Collections;
54  import java.util.List;
55  import java.util.Random;
56  
57  /**
58   * This class provides basic facilities for manipulating files and file paths.
59   * <p/>
60   * <h3>Path-related methods</h3>
61   * <p/>
62   * <p>Methods exist to retrieve the components of a typical file path. For example
63   * <code>/www/hosted/mysite/index.html</code>, can be broken into:
64   * <ul>
65   * <li><code>/www/hosted/mysite/index</code> -- retrievable through {@link #removeExtension}</li>
66   * <li><code>html</code> -- retrievable through {@link #getExtension}</li>
67   * </ul>
68   * </p>
69   * <p/>
70   * <h3>File-related methods</h3>
71   * <p/>
72   * There are methods to  create a {@link #toFile File from a URL}, copy a
73   * copy a {@link #copyFile File to another File},
74   * copy a {@link #copyURLToFile URL's contents to a File},
75   * as well as methods to {@link #deleteDirectory(File) delete} and {@link #cleanDirectory(File)
76   * clean} a directory.
77   * </p>
78   * <p/>
79   * Common {@link java.io.File} manipulation routines.
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   *
86   * @author <a href="mailto:burton@relativity.yi.org">Kevin A. Burton</A>
87   * @author <a href="mailto:sanders@apache.org">Scott Sanders</a>
88   * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
89   * @author <a href="mailto:Christoph.Reck@dlr.de">Christoph.Reck</a>
90   * @author <a href="mailto:peter@apache.org">Peter Donald</a>
91   * @author <a href="mailto:jefft@apache.org">Jeff Turner</a>
92   *
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      * <p/>
486      * The given extensions should be like "java" and not like ".java".
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      * (&quot;&quot;) 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 IOUtil.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 IllegalArgumentException( "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(), CopyOptions.NOFOLLOW_LINKS,
798      *     CopyOptions.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 occured
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      *     CopyOptions.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      *     CopyOptions.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 it's canonical form. If <code>filename</code> is
1026      * relative (doesn't start with <code>/</code>), it will be 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
1030      *                 relative.
1031      * @param filename absolute or relative file path to resolve
1032      * @return the canonical <code>File</code> of <code>filename</code>
1033      */
1034     @Nonnull public static File resolveFile( final File baseFile, @Nonnull String filename )
1035     {
1036         String filenm = filename;
1037         if ( '/' != File.separatorChar )
1038         {
1039             filenm = filename.replace( '/', File.separatorChar );
1040         }
1041 
1042         if ( '\\' != File.separatorChar )
1043         {
1044             filenm = filename.replace( '\\', File.separatorChar );
1045         }
1046 
1047         // deal with absolute files
1048         if ( filenm.startsWith( File.separator ) || ( Os.isFamily( Os.FAMILY_WINDOWS ) && filenm.indexOf( ":" ) > 0 ) )
1049         {
1050             File file = new File( filenm );
1051 
1052             try
1053             {
1054                 file = file.getCanonicalFile();
1055             }
1056             catch ( final IOException ioe )
1057             {
1058                 // nop
1059             }
1060 
1061             return file;
1062         }
1063         // FIXME: I'm almost certain this // removal is unnecessary, as getAbsoluteFile() strips
1064         // them. However, I'm not sure about this UNC stuff. (JT)
1065         final char[] chars = filename.toCharArray();
1066         final StringBuilder sb = new StringBuilder();
1067 
1068         //remove duplicate file separators in succession - except
1069         //on win32 at start of filename as UNC filenames can
1070         //be \\AComputer\AShare\myfile.txt
1071         int start = 0;
1072         if ( '\\' == File.separatorChar )
1073         {
1074             sb.append( filenm.charAt( 0 ) );
1075             start++;
1076         }
1077 
1078         for ( int i = start; i < chars.length; i++ )
1079         {
1080             final boolean doubleSeparator = File.separatorChar == chars[i] && File.separatorChar == chars[i - 1];
1081 
1082             if ( !doubleSeparator )
1083             {
1084                 sb.append( chars[i] );
1085             }
1086         }
1087 
1088         filenm = sb.toString();
1089 
1090         //must be relative
1091         File file = ( new File( baseFile, filenm ) ).getAbsoluteFile();
1092 
1093         try
1094         {
1095             file = file.getCanonicalFile();
1096         }
1097         catch ( final IOException ioe )
1098         {
1099             // nop
1100         }
1101 
1102         return file;
1103     }
1104 
1105     /**
1106      * Delete a file. If file is directory, delete it and all sub-directories.
1107      *
1108      * @param file the file path
1109      * @throws IOException if any
1110      * @deprecated use {@code org.apache.commons.io.FileUtils.deleteQuietly()}
1111      */
1112     @Deprecated
1113     public static void forceDelete( @Nonnull final String file )
1114         throws IOException
1115     {
1116         forceDelete( new File( file ) );
1117     }
1118 
1119     /**
1120      * Delete a file. If file is directory, delete it and all sub-directories.
1121      *
1122      * @param file a file
1123      * @throws IOException if any
1124      * @deprecated use {@code org.apache.commons.io.FileUtils.deleteQuietly()}
1125      */
1126     @Deprecated
1127     public static void forceDelete( @Nonnull final File file )
1128         throws IOException
1129     {
1130         if ( file.isDirectory() )
1131         {
1132             deleteDirectory( file );
1133         }
1134         else
1135         {
1136             /*
1137              * NOTE: Always try to delete the file even if it appears to be non-existent. This will ensure that a
1138              * symlink whose target does not exist is deleted, too.
1139              */
1140             boolean filePresent = file.getCanonicalFile().exists();
1141             if ( !deleteFile( file ) && filePresent )
1142             {
1143                 final String message = "File " + file + " unable to be deleted.";
1144                 throw new IOException( message );
1145             }
1146         }
1147     }
1148 
1149     /**
1150      * Deletes a file.
1151      *
1152      * @param file the file to delete
1153      * @throws IOException if the file cannot be deleted
1154      * @deprecated use {@code java.nio.files.Files.delete(file.toPath())}
1155      */
1156     @Deprecated
1157     public static void delete( @Nonnull File file )
1158         throws IOException
1159     {
1160         Files.delete( file.toPath() );
1161     }
1162 
1163     /**
1164      * @param file the file
1165      * @return true / false
1166      * @deprecated use {@code java.nio.files.Files.delete(file.toPath())}
1167      */
1168     @Deprecated
1169     public static boolean deleteLegacyStyle( @Nonnull File file )
1170     {
1171         try
1172         {
1173             Files.delete( file.toPath() );
1174             return true;
1175         }
1176         catch ( IOException e )
1177         {
1178             return false;
1179         }
1180     }
1181 
1182     /**
1183      * Accommodate Windows bug encountered in both Sun and IBM JDKs.
1184      * Others possible. If the delete does not work, call System.gc(),
1185      * wait a little and try again.
1186      *
1187      * @param file a file
1188      * @throws IOException if any
1189      */
1190     private static boolean deleteFile( @Nonnull File file )
1191         throws IOException
1192     {
1193         if ( file.isDirectory() )
1194         {
1195             throw new IOException( "File " + file + " isn't a file." );
1196         }
1197 
1198         if ( !deleteLegacyStyle( file ) )
1199         {
1200             if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
1201             {
1202                 file = file.getCanonicalFile();
1203             }
1204 
1205             try
1206             {
1207                 Thread.sleep( 10 );
1208                 return deleteLegacyStyle( file );
1209             }
1210             catch ( InterruptedException ex )
1211             {
1212                 return deleteLegacyStyle( file );
1213             }
1214         }
1215 
1216         return true;
1217     }
1218 
1219 
1220     /**
1221      * Make a directory.
1222      *
1223      * @param file not null
1224      * @throws IOException if a file already exists with the specified name or the directory is unable to be created
1225      * @throws IllegalArgumentException if the file contains illegal Windows characters under Windows OS.
1226      * @see #INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME
1227      */
1228     public static void forceMkdir( @Nonnull final File file )
1229         throws IOException
1230     {
1231         if ( Os.isFamily( Os.FAMILY_WINDOWS ) && !isValidWindowsFileName( file ) )
1232         {
1233             throw new IllegalArgumentException(
1234                 "The file (" + file.getAbsolutePath() + ") cannot contain any of the following characters: \n"
1235                     + StringUtils.join( INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME, " " ) );
1236         }
1237 
1238         if ( file.exists() )
1239         {
1240             if ( file.isFile() )
1241             {
1242                 final String message =
1243                     "File " + file + " exists and is " + "not a directory. Unable to create directory.";
1244                 throw new IOException( message );
1245             }
1246         }
1247         else
1248         {
1249             if ( !file.mkdirs() )
1250             {
1251                 final String message = "Unable to create directory " + file;
1252                 throw new IOException( message );
1253             }
1254         }
1255     }
1256 
1257     /**
1258      * Recursively delete a directory.
1259      *
1260      * @param directory a directory
1261      * @throws IOException if any
1262      * @deprecated use {@code org.apache.commons.io.FileUtils.deleteDirectory()}
1263      */
1264     @Deprecated
1265     public static void deleteDirectory( @Nonnull final String directory )
1266         throws IOException
1267     {
1268         deleteDirectory( new File( directory ) );
1269     }
1270 
1271     /**
1272      * Recursively delete a directory.
1273      *
1274      * @param directory a directory
1275      * @throws IOException if any
1276      * @deprecated use {@code org.apache.commons.io.FileUtils.deleteDirectory()}
1277      */
1278     @Deprecated
1279     public static void deleteDirectory( @Nonnull final File directory )
1280         throws IOException
1281     {
1282         if ( !directory.exists() )
1283         {
1284             return;
1285         }
1286 
1287         /* try delete the directory before its contents, which will take
1288          * care of any directories that are really symbolic links.
1289          */
1290         if ( deleteLegacyStyle( directory ) )
1291         {
1292             return;
1293         }
1294 
1295         cleanDirectory( directory );
1296         if ( !deleteLegacyStyle( directory ) )
1297         {
1298             final String message = "Directory " + directory + " unable to be deleted.";
1299             throw new IOException( message );
1300         }
1301     }
1302 
1303     /**
1304      * Remove all files from a directory without deleting it.
1305      *
1306      * @param directory a directory
1307      * @throws IOException if any. This can leave cleaning in a half-finished state where
1308      *         some but not all files have been deleted.
1309      * @deprecated use {@code org.apache.commons.io.FileUtils.cleanDirectory()}
1310      */
1311     @Deprecated
1312     public static void cleanDirectory( @Nonnull final File directory )
1313         throws IOException
1314     {
1315         if ( !directory.exists() )
1316         {
1317             final String message = directory + " does not exist";
1318             throw new IllegalArgumentException( message );
1319         }
1320 
1321         if ( !directory.isDirectory() )
1322         {
1323             final String message = directory + " is not a directory";
1324             throw new IllegalArgumentException( message );
1325         }
1326 
1327         IOException exception = null;
1328 
1329         final File[] files = directory.listFiles();
1330 
1331         if ( files == null )
1332         {
1333             return;
1334         }
1335 
1336         for ( final File file : files )
1337         {
1338             try
1339             {
1340                 forceDelete( file );
1341             }
1342             catch ( final IOException ioe )
1343             {
1344                 exception = ioe;
1345             }
1346         }
1347 
1348         if ( null != exception )
1349         {
1350             throw exception;
1351         }
1352     }
1353 
1354     /**
1355      * Recursively count size of a directory.
1356      *
1357      * @param directory a directory
1358      * @return size of directory in bytes
1359      * @deprecated use {@code org.apache.commons.io.FileUtils.sizeOf()}
1360      */
1361     @Deprecated
1362     public static long sizeOfDirectory( @Nonnull final String directory )
1363     {
1364         return sizeOfDirectory( new File( directory ) );
1365     }
1366 
1367     /**
1368      * Recursively count size of a directory.
1369      *
1370      * @param directory a directory
1371      * @return size of directory in bytes
1372      * @deprecated use {@code org.apache.commons.io.FileUtils.sizeOf()}
1373      */
1374     @Deprecated
1375     public static long sizeOfDirectory( @Nonnull final File directory )
1376     {
1377         if ( !directory.exists() )
1378         {
1379             final String message = directory + " does not exist";
1380             throw new IllegalArgumentException( message );
1381         }
1382 
1383         if ( !directory.isDirectory() )
1384         {
1385             final String message = directory + " is not a directory";
1386             throw new IllegalArgumentException( message );
1387         }
1388 
1389         long size = 0;
1390 
1391         final File[] files = directory.listFiles();
1392         if ( files == null )
1393         {
1394             throw new IllegalArgumentException( "Problems reading directory" );
1395         }
1396 
1397         for ( final File file : files )
1398         {
1399             if ( file.isDirectory() )
1400             {
1401                 size += sizeOfDirectory( file );
1402             }
1403             else
1404             {
1405                 size += file.length();
1406             }
1407         }
1408 
1409         return size;
1410     }
1411 
1412     /**
1413      * Return the files contained in the directory, using inclusion and exclusion Ant patterns,
1414      * including the directory name in each of the files
1415      *
1416      * @param directory the directory to scan
1417      * @param includes  the Ant includes pattern, comma separated
1418      * @param excludes  the Ant excludes pattern, comma separated
1419      * @return a list of File objects
1420      * @throws IOException in case of failure.
1421      * @see #getFileNames(File, String, String, boolean)
1422      */
1423     @Nonnull
1424     public static List<File> getFiles( @Nonnull File directory, @Nullable String includes, @Nullable String excludes )
1425         throws IOException
1426     {
1427         return getFiles( directory, includes, excludes, true );
1428     }
1429 
1430     /**
1431      * Return the files contained in the directory, using inclusion and exclusion Ant patterns
1432      *
1433      * @param directory      the directory to scan
1434      * @param includes       the includes pattern, comma separated
1435      * @param excludes       the excludes pattern, comma separated
1436      * @param includeBasedir true to include the base dir in each file
1437      * @return a list of File objects
1438      * @throws IOException in case of failure.
1439      * @see #getFileNames(File, String, String, boolean)
1440      */
1441     @Nonnull
1442     public static List<File> getFiles( @Nonnull File directory, @Nullable String includes, @Nullable String excludes,
1443                                        boolean includeBasedir )
1444         throws IOException
1445     {
1446         List<String> fileNames = getFileNames( directory, includes, excludes, includeBasedir );
1447 
1448         List<File> files = new ArrayList<File>();
1449 
1450         for ( String filename : fileNames )
1451         {
1452             files.add( new File( filename ) );
1453         }
1454 
1455         return files;
1456     }
1457 
1458     /**
1459      * Return a list of files as String depending options.
1460      * This method use case sensitive file name.
1461      *
1462      * @param directory      the directory to scan
1463      * @param includes       the Ant includes pattern, comma separated
1464      * @param excludes       the Ant excludes pattern, comma separated
1465      * @param includeBasedir true to include the base directory in each String of file
1466      * @return a list of file names
1467      * @throws IOException in case of failure
1468      */
1469     @Nonnull public static List<String> getFileNames( @Nonnull File directory, @Nullable String includes,
1470                                                       @Nullable String excludes, boolean includeBasedir )
1471         throws IOException
1472     {
1473         return getFileNames( directory, includes, excludes, includeBasedir, true );
1474     }
1475 
1476     /**
1477      * Return a list of files as String depending options.
1478      *
1479      * @param directory       the directory to scan
1480      * @param includes        the Ant includes pattern, comma separated
1481      * @param excludes        the Ant excludes pattern, comma separated
1482      * @param includeBasedir  true to include the base dir in each String of file
1483      * @param isCaseSensitive true if case sensitive
1484      * @return a list of files as String
1485      * @throws IOException
1486      */
1487     @Nonnull private static List<String> getFileNames( @Nonnull File directory, @Nullable String includes,
1488                                                        @Nullable String excludes, boolean includeBasedir,
1489                                                        boolean isCaseSensitive )
1490         throws IOException
1491     {
1492         return getFileAndDirectoryNames( directory, includes, excludes, includeBasedir, isCaseSensitive, true, false );
1493     }
1494 
1495     /**
1496      * Return a list of directories as String depending options.
1497      * This method use case sensitive file name.
1498      *
1499      * @param directory      the directory to scan
1500      * @param includes       the Ant includes pattern, comma separated
1501      * @param excludes       the Ant excludes pattern, comma separated
1502      * @param includeBasedir true to include the base dir in each String of file
1503      * @return a list of directories as String
1504      * @throws IOException in case of failure.
1505      */
1506     @Nonnull public static List<String> getDirectoryNames( @Nonnull File directory, @Nullable String includes,
1507                                                            @Nullable String excludes, boolean includeBasedir )
1508         throws IOException
1509     {
1510         return getDirectoryNames( directory, includes, excludes, includeBasedir, true );
1511     }
1512 
1513     /**
1514      * Return a list of directories as Strings.
1515      *
1516      * @param directory       the directory to scan
1517      * @param includes        the Ant includes pattern, comma separated
1518      * @param excludes        the Ant excludes pattern, comma separated
1519      * @param includeBasedir  true to include the base directory in each String of file
1520      * @param isCaseSensitive true if case sensitive
1521      * @return a list of directories as String
1522      * @throws IOException in case of failure
1523      */
1524     @Nonnull public static List<String> getDirectoryNames( @Nonnull File directory, @Nullable String includes,
1525                                                            @Nullable String excludes, boolean includeBasedir,
1526                                                            boolean isCaseSensitive )
1527         throws IOException
1528     {
1529         return getFileAndDirectoryNames( directory, includes, excludes, includeBasedir, isCaseSensitive, false, true );
1530     }
1531 
1532     /**
1533      * Return a list of file names as Strings.
1534      *
1535      * @param directory       the directory to scan
1536      * @param includes        the Ant includes pattern, comma separated
1537      * @param excludes        the Ant excludes pattern, comma separated
1538      * @param includeBasedir  true to include the base directory in each String of file
1539      * @param isCaseSensitive true if case sensitive
1540      * @param getFiles        true to include regular files
1541      * @param getDirectories  true to include directories
1542      * @return a list of file names
1543      */
1544     @Nonnull public static List<String> getFileAndDirectoryNames( File directory, @Nullable String includes,
1545                                                                   @Nullable String excludes, boolean includeBasedir,
1546                                                                   boolean isCaseSensitive, boolean getFiles,
1547                                                                   boolean getDirectories )
1548     {
1549         DirectoryScanner scanner = new DirectoryScanner();
1550 
1551         scanner.setBasedir( directory );
1552 
1553         if ( includes != null )
1554         {
1555             scanner.setIncludes( StringUtils.split( includes, "," ) );
1556         }
1557 
1558         if ( excludes != null )
1559         {
1560             scanner.setExcludes( StringUtils.split( excludes, "," ) );
1561         }
1562 
1563         scanner.setCaseSensitive( isCaseSensitive );
1564 
1565         scanner.scan();
1566 
1567         List<String> list = new ArrayList<String>();
1568 
1569         if ( getFiles )
1570         {
1571             String[] files = scanner.getIncludedFiles();
1572 
1573             for ( String file : files )
1574             {
1575                 if ( includeBasedir )
1576                 {
1577                     list.add( directory + FileUtils.FS + file );
1578                 }
1579                 else
1580                 {
1581                     list.add( file );
1582                 }
1583             }
1584         }
1585 
1586         if ( getDirectories )
1587         {
1588             String[] directories = scanner.getIncludedDirectories();
1589 
1590             for ( String directory1 : directories )
1591             {
1592                 if ( includeBasedir )
1593                 {
1594                     list.add( directory + FileUtils.FS + directory1 );
1595                 }
1596                 else
1597                 {
1598                     list.add( directory1 );
1599                 }
1600             }
1601         }
1602 
1603         return list;
1604     }
1605 
1606     /**
1607      * Copy the contents of a directory into another one.
1608      *
1609      * @param sourceDirectory      the source directory
1610      * @param destinationDirectory the target directory
1611      * @throws IOException if any
1612      */
1613     public static void copyDirectory( @Nonnull File sourceDirectory, @Nonnull File destinationDirectory )
1614         throws IOException
1615     {
1616         copyDirectory( sourceDirectory, destinationDirectory, "**", null );
1617     }
1618 
1619     /**
1620      * Copy the contents of a directory into another one.
1621      *
1622      * @param sourceDirectory      the source directory
1623      * @param destinationDirectory the target directory
1624      * @param includes             Ant include pattern
1625      * @param excludes             Ant exclude pattern
1626      * @throws IOException if any
1627      * @see #getFiles(File, String, String)
1628      */
1629     public static void copyDirectory( @Nonnull File sourceDirectory, @Nonnull File destinationDirectory,
1630                                       @Nullable String includes, @Nullable String excludes )
1631         throws IOException
1632     {
1633         if ( !sourceDirectory.exists() )
1634         {
1635             return;
1636         }
1637 
1638         List<File> files = getFiles( sourceDirectory, includes, excludes );
1639 
1640         for ( File file : files )
1641         {
1642             copyFileToDirectory( file, destinationDirectory );
1643         }
1644     }
1645 
1646     /**
1647      * Copies an entire directory structure.
1648      * <p/>
1649      * Note:
1650      * <ul>
1651      * <li>It will include empty directories.
1652      * <li>The <code>sourceDirectory</code> must exist.
1653      * </ul>
1654      *
1655      * @param sourceDirectory      the source dir
1656      * @param destinationDirectory the target dir
1657      * @throws IOException if any
1658      * @deprecated use {@code org.apache.commons.io.FileUtils.copyDirectory()}
1659      */
1660     @Deprecated
1661     public static void copyDirectoryStructure( @Nonnull File sourceDirectory, @Nonnull File destinationDirectory )
1662         throws IOException
1663     {
1664         copyDirectoryStructure( sourceDirectory, destinationDirectory, destinationDirectory, false );
1665     }
1666 
1667     private static void copyDirectoryStructure( @Nonnull File sourceDirectory, @Nonnull File destinationDirectory,
1668                                                 File rootDestinationDirectory, boolean onlyModifiedFiles )
1669         throws IOException
1670     {
1671         //noinspection ConstantConditions
1672         if ( sourceDirectory == null )
1673         {
1674             throw new IOException( "source directory can't be null." );
1675         }
1676 
1677         //noinspection ConstantConditions
1678         if ( destinationDirectory == null )
1679         {
1680             throw new IOException( "destination directory can't be null." );
1681         }
1682 
1683         if ( sourceDirectory.equals( destinationDirectory ) )
1684         {
1685             throw new IOException( "source and destination are the same directory." );
1686         }
1687 
1688         if ( !sourceDirectory.exists() )
1689         {
1690             throw new IOException( "Source directory doesn't exists (" + sourceDirectory.getAbsolutePath() + ")." );
1691         }
1692 
1693         File[] files = sourceDirectory.listFiles();
1694 
1695         if ( files == null )
1696         {
1697             return;
1698         }
1699 
1700         String sourcePath = sourceDirectory.getAbsolutePath();
1701 
1702         for ( File file : files )
1703         {
1704             if ( file.equals( rootDestinationDirectory ) )
1705             {
1706                 // We don't copy the destination directory in itself
1707                 continue;
1708             }
1709 
1710             String dest = file.getAbsolutePath();
1711 
1712             dest = dest.substring( sourcePath.length() + 1 );
1713 
1714             File destination = new File( destinationDirectory, dest );
1715 
1716             if ( file.isFile() )
1717             {
1718                 destination = destination.getParentFile();
1719 
1720                 if ( onlyModifiedFiles )
1721                 {
1722                     copyFileToDirectoryIfModified( file, destination );
1723                 }
1724                 else
1725                 {
1726                     copyFileToDirectory( file, destination );
1727                 }
1728             }
1729             else if ( file.isDirectory() )
1730             {
1731                 if ( !destination.exists() && !destination.mkdirs() )
1732                 {
1733                     throw new IOException(
1734                         "Could not create destination directory '" + destination.getAbsolutePath() + "'." );
1735                 }
1736 
1737                 copyDirectoryStructure( file, destination, rootDestinationDirectory, onlyModifiedFiles );
1738             }
1739             else
1740             {
1741                 throw new IOException( "Unknown file type: " + file.getAbsolutePath() );
1742             }
1743         }
1744     }
1745 
1746     /**
1747      * Renames a file, even if that involves crossing file system boundaries.
1748      * <p/>
1749      * <p>This will remove <code>to</code> (if it exists), ensure that
1750      * <code>to</code>'s parent directory exists and move
1751      * <code>from</code>, which involves deleting <code>from</code> as
1752      * well.</p>
1753      *
1754      * @param from the file to move
1755      * @param to   the new file name
1756      * @throws IOException if anything bad happens during this process.
1757      *                     Note that <code>to</code> may have been deleted already when this happens.
1758      * @deprecated use {@code java.nio.Files.move()}
1759      */
1760     @Deprecated
1761     public static void rename( @Nonnull File from, @Nonnull File to )
1762         throws IOException
1763     {
1764         if ( to.exists() && !deleteLegacyStyle( to ) )
1765         {
1766             throw new IOException( "Failed to delete " + to + " while trying to rename " + from );
1767         }
1768 
1769         File parent = to.getParentFile();
1770         if ( parent != null && !parent.exists() && !parent.mkdirs() )
1771         {
1772             throw new IOException( "Failed to create directory " + parent + " while trying to rename " + from );
1773         }
1774 
1775         if ( !from.renameTo( to ) )
1776         {
1777             copyFile( from, to );
1778             if ( !deleteLegacyStyle( from ) )
1779             {
1780                 throw new IOException( "Failed to delete " + from + " while trying to rename it." );
1781             }
1782         }
1783     }
1784 
1785     /**
1786      * Create a temporary file in a given directory.
1787      * <p/>
1788      * <p>The file denoted by the returned abstract pathname did not
1789      * exist before this method was invoked, any subsequent invocation
1790      * of this method will yield a different file name.</p>
1791      * <p>
1792      * The filename is prefixNNNNNsuffix where NNNN is a random number
1793      * </p>
1794      * <p>This method is different to {@link File#createTempFile(String, String, File)}
1795      * as it doesn't create the file itself.
1796      * It uses the location pointed to by java.io.tmpdir
1797      * when the parentDir attribute is null.</p>
1798      * <p>To automatically delete the file created by this method, use the
1799      * {@link File#deleteOnExit()} method.</p>
1800      *
1801      * @param prefix    prefix before the random number
1802      * @param suffix    file extension; include the '.'
1803      * @param parentDir directory to create the temporary file in <code>-java.io.tmpdir</code>
1804      *                  used if not specified
1805      * @return a File reference to the new temporary file.
1806      * @deprecated use {@code java.nio.Files.createTempFile()}
1807      */
1808     @Deprecated
1809     public static File createTempFile( @Nonnull String prefix, @Nonnull String suffix, @Nullable File parentDir )
1810     {
1811         File result;
1812         String parent = System.getProperty( "java.io.tmpdir" );
1813         if ( parentDir != null )
1814         {
1815             parent = parentDir.getPath();
1816         }
1817         DecimalFormat fmt = new DecimalFormat( "#####" );
1818         SecureRandom secureRandom = new SecureRandom();
1819         long secureInitializer = secureRandom.nextLong();
1820         Random rand = new Random( secureInitializer + Runtime.getRuntime().freeMemory() );
1821         do
1822         {
1823             result = new File( parent, prefix + fmt.format( positiveRandom( rand ) ) + suffix );
1824         }
1825         while ( result.exists() );
1826 
1827         return result;
1828     }
1829 
1830     private static int positiveRandom( Random rand )
1831     {
1832         int a = rand.nextInt();
1833         while ( a == Integer.MIN_VALUE )
1834         {
1835             a = rand.nextInt();
1836         }
1837         return Math.abs( a );
1838     }
1839 
1840     /**
1841      * <b>If wrappers is null or empty, the file will be copied only if to.lastModified() < from.lastModified()</b>
1842      *
1843      * @param from     the file to copy
1844      * @param to       the destination file
1845      * @param encoding the file output encoding (only if wrappers is not empty)
1846      * @param wrappers array of {@link FilterWrapper}
1847      * @throws IOException if an IO error occurs during copying or filtering
1848      */
1849     public static void copyFile( @Nonnull File from, @Nonnull File to, @Nullable String encoding,
1850                                  @Nullable FilterWrapper... wrappers )
1851         throws IOException
1852     {
1853         copyFile( from, to, encoding, wrappers, false );
1854     }
1855 
1856     /**
1857      * Wrapper class for Filter.
1858      */
1859     public abstract static class FilterWrapper
1860     {
1861         /**
1862          * @param fileReader {@link Reader}
1863          * @return the Reader instance
1864          */
1865         public abstract Reader getReader( Reader fileReader );
1866     }
1867 
1868     /**
1869      * <b>If wrappers is null or empty, the file will be copy only if to.lastModified() < from.lastModified() or if
1870      * overwrite is true</b>
1871      *
1872      * @param from the file to copy
1873      * @param to the destination file
1874      * @param encoding the file output encoding (only if wrappers is not empty)
1875      * @param wrappers array of {@link FilterWrapper}
1876      * @param overwrite if true and wrappers is null or empty, the file will be copied even if
1877      *         to.lastModified() < from.lastModified()
1878      * @throws IOException if an IO error occurs during copying or filtering
1879      */
1880     public static void copyFile( @Nonnull File from, @Nonnull File to, @Nullable String encoding,
1881                                  @Nullable FilterWrapper[] wrappers, boolean overwrite )
1882         throws IOException
1883     {
1884         if ( wrappers == null || wrappers.length == 0 )
1885         {
1886             if ( overwrite || to.lastModified() < from.lastModified() )
1887             {
1888                 copyFile( from, to );
1889             }
1890         }
1891         else
1892         {
1893             Charset charset = charset( encoding );
1894 
1895             // buffer so it isn't reading a byte at a time!
1896             try ( Reader fileReader = Files.newBufferedReader( from.toPath(), charset ) )
1897             {
1898                 Reader wrapped = fileReader;
1899                 for ( FilterWrapper wrapper : wrappers )
1900                 {
1901                     wrapped = wrapper.getReader( wrapped );
1902                 }
1903 
1904                 if ( overwrite || !to.exists() )
1905                 {
1906                     try ( Writer fileWriter = Files.newBufferedWriter( to.toPath(), charset ) )
1907                     {
1908                         IOUtil.copy( wrapped, fileWriter );
1909                     }
1910                 }
1911                 else
1912                 {
1913                     CharsetEncoder encoder = charset.newEncoder();
1914 
1915                     int totalBufferSize = FILE_COPY_BUFFER_SIZE;
1916 
1917                     int charBufferSize = ( int ) Math.floor( totalBufferSize / ( 2 + 2 * encoder.maxBytesPerChar() ) );
1918                     int byteBufferSize = ( int ) Math.ceil( charBufferSize * encoder.maxBytesPerChar() );
1919 
1920                     CharBuffer newChars = CharBuffer.allocate( charBufferSize );
1921                     ByteBuffer newBytes = ByteBuffer.allocate( byteBufferSize );
1922                     ByteBuffer existingBytes = ByteBuffer.allocate( byteBufferSize );
1923 
1924                     CoderResult coderResult;
1925                     int existingRead;
1926                     boolean writing = false;
1927 
1928                     try ( final RandomAccessFile existing = new RandomAccessFile( to, "rw" ) )
1929                     {
1930                         int n;
1931                         while ( -1 != ( n = wrapped.read( newChars ) ) )
1932                         {
1933                             ( ( Buffer ) newChars ).flip();
1934 
1935                             coderResult = encoder.encode( newChars, newBytes, n != 0 );
1936                             if ( coderResult.isError() )
1937                             {
1938                                 coderResult.throwException();
1939                             }
1940 
1941                             ( ( Buffer ) newBytes ).flip();
1942 
1943                             if ( !writing )
1944                             {
1945                                 existingRead = existing.read( existingBytes.array(), 0, newBytes.remaining() );
1946                                 ( ( Buffer ) existingBytes ).position( existingRead );
1947                                 ( ( Buffer ) existingBytes ).flip();
1948 
1949                                 if ( newBytes.compareTo( existingBytes ) != 0 )
1950                                 {
1951                                     writing = true;
1952                                     if ( existingRead > 0 )
1953                                     {
1954                                         existing.seek( existing.getFilePointer() - existingRead );
1955                                     }
1956                                 }
1957                             }
1958 
1959                             if ( writing )
1960                             {
1961                                 existing.write( newBytes.array(), 0, newBytes.remaining() );
1962                             }
1963 
1964                             ( ( Buffer ) newChars ).clear();
1965                             ( ( Buffer ) newBytes ).clear();
1966                             ( ( Buffer ) existingBytes ).clear();
1967                         }
1968 
1969                         if ( existing.length() > existing.getFilePointer() )
1970                         {
1971                             existing.setLength( existing.getFilePointer() );
1972                         }
1973                     }
1974                 }
1975             }
1976         }
1977 
1978         copyFilePermissions( from, to );
1979     }
1980 
1981     /**
1982      * Attempts to copy file permissions from the source to the destination file.
1983      * Initially attempts to copy posix file permissions, assuming that the files are both on posix filesystems.
1984      * If the initial attempts fail then a second attempt using less precise permissions model.
1985      * Note that permissions are copied on a best-efforts basis,
1986      * failure to copy permissions will not result in an exception.
1987      *
1988      * @param source the file to copy permissions from.
1989      * @param destination the file to copy permissions to.
1990      */
1991     private static void copyFilePermissions( @Nonnull File source, @Nonnull File destination )
1992         throws IOException
1993     {
1994         try
1995         {
1996             // attempt to copy posix file permissions
1997             Files.setPosixFilePermissions(
1998                 destination.toPath(),
1999                 Files.getPosixFilePermissions( source.toPath() )
2000             );
2001         }
2002         catch ( UnsupportedOperationException e )
2003         {
2004             // fallback to setting partial permissions
2005             destination.setExecutable( source.canExecute() );
2006             destination.setReadable( source.canRead() );
2007             destination.setWritable( source.canWrite() );
2008         }
2009     }
2010 
2011     /**
2012      * Note: the file content is read with platform encoding.
2013      *
2014      * @param file the file
2015      * @return a List containing every every line not starting with # and not empty
2016      * @throws IOException if any
2017      * @deprecated assumes the platform default character set
2018      */
2019     @Deprecated
2020     @Nonnull public static List<String> loadFile( @Nonnull File file )
2021         throws IOException
2022     {
2023         List<String> lines = new ArrayList<String>();
2024 
2025         if ( file.exists() )
2026         {
2027             try ( BufferedReader reader = Files.newBufferedReader( file.toPath(), Charset.defaultCharset() ) )
2028             {
2029                 for ( String line = reader.readLine(); line != null; line = reader.readLine() )
2030                 {
2031                     line = line.trim();
2032                     if ( !line.startsWith( "#" ) && line.length() != 0 )
2033                     {
2034                         lines.add( line );
2035                     }
2036                 }
2037             }
2038         }
2039 
2040         return lines;
2041 
2042     }
2043 
2044     /**
2045      * Returns the named charset or the default charset.
2046      * @param encoding the name or alias of the charset, null or empty
2047      * @return A charset object for the named or default charset.
2048      */
2049     private static Charset charset( String encoding )
2050     {
2051         if ( encoding == null || encoding.isEmpty() )
2052         {
2053             return Charset.defaultCharset();
2054         }
2055         else
2056         {
2057             return Charset.forName( encoding );
2058         }
2059     }
2060 
2061     /**
2062      * For Windows OS, check if the file name contains any of the following characters:
2063      * <code>":", "*", "?", "\"", "<", ">", "|"</code>
2064      *
2065      * @param f not null file
2066      * @return <code>false</code> if the file path contains any of forbidden Windows characters,
2067      * <code>true</code> if the Os is not Windows or if the file path respect the Windows constraints.
2068      * @see #INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME
2069      */
2070     private static boolean isValidWindowsFileName( @Nonnull File f )
2071     {
2072         if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
2073         {
2074             if ( StringUtils.indexOfAny( f.getName(), INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME ) != -1 )
2075             {
2076                 return false;
2077             }
2078 
2079             if ( f.getParentFile() != null )
2080             {
2081                 return isValidWindowsFileName( f.getParentFile() );
2082             }
2083         }
2084 
2085         return true;
2086     }
2087 
2088     /**
2089      * Checks whether a given file is a symbolic link.
2090      *
2091      * @param file the file to check
2092      * @throws IOException in case of failure.
2093      * @return true if symbolic link false otherwise.
2094      * @deprecated use {@code java.nio.file.Files.isSymbolicLink(file.toPath())}
2095      */
2096     @Deprecated
2097     public static boolean isSymbolicLink( @Nonnull final File file )
2098         throws IOException
2099     {
2100         return Files.isSymbolicLink( file.toPath() );
2101     }
2102 
2103     /**
2104      * Checks whether a given file is a symbolic link.
2105      *
2106      * @param file the file to check
2107      * @return true if and only if we reliably can say this is a symlink
2108      *
2109      * @throws IOException in case of failure
2110      * @deprecated use {@code java.nio.file.Files.isSymbolicLink(file.toPath())}
2111      */
2112     @Deprecated
2113     public static boolean isSymbolicLinkForSure( @Nonnull final File file )
2114         throws IOException
2115     {
2116         return Files.isSymbolicLink( file.toPath() );
2117     }
2118 
2119     /**
2120      * Create a new symbolic link, possibly replacing an existing symbolic link.
2121      * 
2122      * @param symlink the link name
2123      * @param target the target
2124      * @return the linked file
2125      * @throws IOException in case of an error
2126      * @see {@code java.nio.file.Files.createSymbolicLink(Path)} which creates a new
2127      *         symbolic link but does not replace exsiting symbolic links
2128      */
2129     @Nonnull public static File createSymbolicLink( @Nonnull File symlink,  @Nonnull File target )
2130             throws IOException
2131     {
2132         final Path symlinkPath = symlink.toPath();
2133 
2134         if ( Files.exists( symlinkPath ) )
2135         {
2136             if ( target.equals( Files.readSymbolicLink( symlinkPath ).toFile() ) )
2137             {
2138                 return symlink;
2139             }
2140 
2141             Files.delete( symlinkPath );
2142         }
2143 
2144         return Files.createSymbolicLink( symlinkPath, target.toPath() ).toFile();
2145     }
2146 }