All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.codehaus.plexus.util.FileUtils Maven / Gradle / Ivy

There is a newer version: 3.0.0-alpha-3
Show newest version
package org.codehaus.plexus.util;

/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2001 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.codehaus.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Apache" and "Apache Software Foundation" and
 *    "Apache Turbine" must not be used to endorse or promote products
 *    derived from this software without prior written permission. For
 *    written permission, please contact [email protected].
 *
 * 5. Products derived from this software may not be called "Apache",
 *    "Apache Turbine", nor may "Apache" appear in their name, without
 *    prior written permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * .
 *
 */

import org.codehaus.plexus.util.io.InputStreamFacade;
import org.codehaus.plexus.util.io.URLInputStreamFacade;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.security.SecureRandom;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

/**
 * 

This class provides basic facilities for manipulating files and file paths.

* * Path-related methods * *

Methods exist to retrieve the components of a typical file path. For example * /www/hosted/mysite/index.html, can be broken into: *

    *
  • /www/hosted/mysite/ -- retrievable through {@link #getPath}
  • *
  • index.html -- retrievable through {@link #removePath}
  • *
  • /www/hosted/mysite/index -- retrievable through {@link #removeExtension}
  • *
  • html -- retrievable through {@link #getExtension}
  • *
*

There are also methods to {@link #catPath concatenate two paths}, {@link #resolveFile resolve a path relative to a * File} and {@link #normalize} a path.

* File-related methods * *

There are methods to create a {@link #toFile File from a URL}, copy a {@link #copyFileToDirectory File to a * directory}, copy a {@link #copyFile File to another File}, copy a {@link #copyURLToFile URL's contents to a File}, as * well as methods to {@link #deleteDirectory(File) delete} and {@link #cleanDirectory(File) clean} a directory.

* *

Common {@link java.io.File} manipulation routines.

* *

Taken from the commons-utils repo. Also code from Alexandria's FileUtils. And from Avalon Excalibur's IO. And from * Ant.

* * @author Kevin A. Burton * @author Scott Sanders * @author Daniel Rall * @author Christoph.Reck * @author Peter Donald * @author Jeff Turner * */ public class FileUtils extends BaseFileUtils { /** * The number of bytes in a kilobyte. */ public static final int ONE_KB = 1024; /** * The number of bytes in a megabyte. */ public static final int ONE_MB = ONE_KB * ONE_KB; /** * The number of bytes in a gigabyte. */ public static final int ONE_GB = ONE_KB * ONE_MB; /** * The vm file separator */ public static String FS = File.separator; /** * Non-valid Characters for naming files, folders under Windows: ":", "*", "?", "\"", "<", ">", "|" * * @see * http://support.microsoft.com/?scid=kb%3Ben-us%3B177506&x=12&y=13 */ private static final String[] INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME = { ":", "*", "?", "\"", "<", ">", "|" }; /** * @return the default excludes pattern * @see DirectoryScanner#DEFAULTEXCLUDES */ public static String[] getDefaultExcludes() { return DirectoryScanner.DEFAULTEXCLUDES; } /** * @return the default excludes pattern as list. * @see #getDefaultExcludes() */ public static List getDefaultExcludesAsList() { return Arrays.asList( getDefaultExcludes() ); } /** * @return the default excludes pattern as comma separated string. * @see DirectoryScanner#DEFAULTEXCLUDES * @see StringUtils#join(Object[], String) */ public static String getDefaultExcludesAsString() { return StringUtils.join( DirectoryScanner.DEFAULTEXCLUDES, "," ); } /** * Returns a human-readable version of the file size (original is in bytes). * * @param size The number of bytes. * @return A human-readable display value (includes units). */ public static String byteCountToDisplaySize( int size ) { String displaySize; if ( size / ONE_GB > 0 ) { displaySize = String.valueOf( size / ONE_GB ) + " GB"; } else if ( size / ONE_MB > 0 ) { displaySize = String.valueOf( size / ONE_MB ) + " MB"; } else if ( size / ONE_KB > 0 ) { displaySize = String.valueOf( size / ONE_KB ) + " KB"; } else { displaySize = String.valueOf( size ) + " bytes"; } return displaySize; } /** * Returns the directory path portion of a file specification string. Matches the equally named unix command. * * @param filename the file path * @return The directory portion excluding the ending file separator. */ public static String dirname( String filename ) { int i = filename.lastIndexOf( File.separator ); return ( i >= 0 ? filename.substring( 0, i ) : "" ); } /** * Returns the filename portion of a file specification string. * * @param filename the file path * @return The filename string with extension. */ public static String filename( String filename ) { int i = filename.lastIndexOf( File.separator ); return ( i >= 0 ? filename.substring( i + 1 ) : filename ); } /** * Returns the filename portion of a file specification string. Matches the equally named unix command. * * @param filename the file path * @return The filename string without extension. */ public static String basename( String filename ) { return basename( filename, extension( filename ) ); } /** * Returns the filename portion of a file specification string. Matches the equally named unix command. * * @param filename the file path * @param suffix the file suffix * @return the basename of the file */ public static String basename( String filename, String suffix ) { int i = filename.lastIndexOf( File.separator ) + 1; int lastDot = ( ( suffix != null ) && ( suffix.length() > 0 ) ) ? filename.lastIndexOf( suffix ) : -1; if ( lastDot >= 0 ) { return filename.substring( i, lastDot ); } else if ( i > 0 ) { return filename.substring( i ); } else { return filename; // else returns all (no path and no extension) } } /** * Returns the extension portion of a file specification string. This everything after the last dot '.' in the * filename (NOT including the dot). * * @param filename the file path * @return the extension of the file */ public static String extension( String filename ) { // Ensure the last dot is after the last file separator int lastSep = filename.lastIndexOf( File.separatorChar ); int lastDot; if ( lastSep < 0 ) { lastDot = filename.lastIndexOf( '.' ); } else { lastDot = filename.substring( lastSep + 1 ).lastIndexOf( '.' ); if ( lastDot >= 0 ) { lastDot += lastSep + 1; } } if ( lastDot >= 0 && lastDot > lastSep ) { return filename.substring( lastDot + 1 ); } return ""; } /** * Check if a file exits. * * @param fileName the file path. * @return true if file exists. */ public static boolean fileExists( String fileName ) { File file = new File( fileName ); return file.exists(); } /** * Note: the file content is read with platform encoding. * * @param file the file path * @return the file content using the platform encoding. * @throws IOException if any */ public static String fileRead( String file ) throws IOException { return fileRead( file, null ); } /** * @param file the file path * @param encoding the wanted encoding * @return the file content using the specified encoding. * @throws IOException if any */ public static String fileRead( String file, String encoding ) throws IOException { return fileRead( Paths.get( file ), encoding ); } /** * Note: the file content is read with platform encoding * * @param file the file path * @return the file content using the platform encoding. * @throws IOException if any */ public static String fileRead( File file ) throws IOException { return fileRead( file, null ); } /** * @param file the file path * @param encoding the wanted encoding * @return the file content using the specified encoding. * @throws IOException if any */ public static String fileRead( File file, String encoding ) throws IOException { return fileRead( file.toPath(), encoding ); } /** * Appends data to a file. The file will be created if it does not exist. Note: the data is written with platform * encoding * * @param fileName The path of the file to write. * @param data The content to write to the file. * @throws IOException if any * @deprecated use {@code java.nio.files.Files.write(filename, data.getBytes(encoding), * StandardOpenOption.APPEND, StandardOpenOption.CREATE)} */ public static void fileAppend( String fileName, String data ) throws IOException { fileAppend( fileName, null, data ); } /** * Appends data to a file. The file will be created if it does not exist. * * @param fileName The path of the file to write. * @param encoding The encoding of the file. * @param data The content to write to the file. * @throws IOException if any * @deprecated use {@code java.nio.files.Files.write(filename, data.getBytes(encoding), * StandardOpenOption.APPEND, StandardOpenOption.CREATE)} */ public static void fileAppend( String fileName, String encoding, String data ) throws IOException { fileAppend( Paths.get( fileName), encoding, data ); } private static void fileAppend( Path path, String encoding, String data ) throws IOException { fileWrite( path, encoding, data, StandardOpenOption.APPEND, StandardOpenOption.CREATE ); } /** * Writes data to a file. The file will be created if it does not exist. Note: the data is written with platform * encoding * * @param fileName The path of the file to write. * @param data The content to write to the file. * @throws IOException if any */ public static void fileWrite( String fileName, String data ) throws IOException { fileWrite( fileName, null, data ); } /** * Writes data to a file. The file will be created if it does not exist. * * @param fileName The path of the file to write. * @param encoding The encoding of the file. * @param data The content to write to the file. * @throws IOException if any */ public static void fileWrite( String fileName, String encoding, String data ) throws IOException { Path file = ( fileName == null ) ? null : Paths.get( fileName ); fileWrite( file, encoding, data ); } /** * Writes data to a file. The file will be created if it does not exist. Note: the data is written with platform * encoding * * @param file The file to write. * @param data The content to write to the file. * @throws IOException if any * @since 2.0.6 */ public static void fileWrite( File file, String data ) throws IOException { fileWrite( file, null, data ); } /** * Writes data to a file. The file will be created if it does not exist. * * @param file The file to write. * @param encoding The encoding of the file. * @param data The content to write to the file. * @throws IOException if any * @since 2.0.6 */ public static void fileWrite( File file, String encoding, String data ) throws IOException { fileWrite( file.toPath(), encoding, data ); } /** * Deletes a file. * * @param fileName The path of the file to delete. */ public static void fileDelete( String fileName ) { File file = new File( fileName ); try { NioFiles.deleteIfExists( file ); } catch ( IOException e ) { throw new RuntimeException( e ); } } /** * Waits for NFS to propagate a file creation, imposing a timeout. * * @param fileName The path of the file. * @param seconds The maximum time in seconds to wait. * @return True if file exists. */ public static boolean waitFor( String fileName, int seconds ) { return waitFor( new File( fileName ), seconds ); } /** * Waits for NFS to propagate a file creation, imposing a timeout. * * @param file The file. * @param seconds The maximum time in seconds to wait. * @return True if file exists. */ public static boolean waitFor( File file, int seconds ) { int timeout = 0; int tick = 0; while ( !file.exists() ) { if ( tick++ >= 10 ) { tick = 0; if ( timeout++ > seconds ) { return false; } } try { Thread.sleep( 100 ); } catch ( InterruptedException ignore ) { // nop } } return true; } /** * Creates a file handle. * * @param fileName The path of the file. * @return A File manager. */ public static File getFile( String fileName ) { return new File( fileName ); } /** *

Given a directory and an array of extensions return an array of compliant files.

* *

TODO Should an ignore list be passed in? TODO Should a recurse flag be passed in?

* *

The given extensions should be like "java" and not like ".java"

* * @param directory The path of the directory. * @param extensions an array of expected extensions. * @return An array of files for the wanted extensions. */ public static String[] getFilesFromExtension( String directory, String[] extensions ) { List files = new ArrayList(); File currentDir = new File( directory ); String[] unknownFiles = currentDir.list(); if ( unknownFiles == null ) { return new String[0]; } for ( String unknownFile : unknownFiles ) { String currentFileName = directory + System.getProperty( "file.separator" ) + unknownFile; File currentFile = new File( currentFileName ); if ( currentFile.isDirectory() ) { // ignore all CVS directories... if ( currentFile.getName().equals( "CVS" ) ) { continue; } // ok... transverse into this directory and get all the files... then combine // them with the current list. String[] fetchFiles = getFilesFromExtension( currentFileName, extensions ); files = blendFilesToVector( files, fetchFiles ); } else { // ok... add the file String add = currentFile.getAbsolutePath(); if ( isValidFile( add, extensions ) ) { files.add( add ); } } } // ok... move the Vector into the files list... return files.toArray( new String[0] ); } /** * Private helper method for getFilesFromExtension() */ private static List blendFilesToVector( List v, String[] files ) { for ( String file : files ) { v.add( file ); } return v; } /** * Checks to see if a file is of a particular type(s). Note that if the file does not have an extension, an empty * string ("") is matched for. */ private static boolean isValidFile( String file, String[] extensions ) { String extension = extension( file ); if ( extension == null ) { extension = ""; } // ok.. now that we have the "extension" go through the current know // excepted extensions and determine if this one is OK. for ( String extension1 : extensions ) { if ( extension1.equals( extension ) ) { return true; } } return false; } /** * Simple way to make a directory * * @param dir the directory to create * @throws IllegalArgumentException if the dir contains illegal Windows characters under Windows OS. * @see #INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME */ public static void mkdir( String dir ) { File file = new File( dir ); if ( Os.isFamily( Os.FAMILY_WINDOWS ) && !isValidWindowsFileName( file ) ) { throw new IllegalArgumentException( "The file (" + dir + ") cannot contain any of the following characters: \n" + StringUtils.join( INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME, " " ) ); } if ( !file.exists() ) { file.mkdirs(); } } /** * Compare the contents of two files to determine if they are equal or not. * * @param file1 the first file * @param file2 the second file * @return true if the content of the files are equal or they both don't exist, false otherwise * @throws IOException if any */ public static boolean contentEquals( final File file1, final File file2 ) throws IOException { final boolean file1Exists = file1.exists(); if ( file1Exists != file2.exists() ) { return false; } if ( !file1Exists ) { // two not existing files are equal return true; } if ( file1.isDirectory() || file2.isDirectory() ) { // don't want to compare directory contents return false; } try ( InputStream input1 = Files.newInputStream( file1.toPath() ); InputStream input2 = Files.newInputStream( file2.toPath() ) ) { return IOUtil.contentEquals( input1, input2 ); } } /** * Convert from a URL to a File. * * @param url File URL. * @return The equivalent File object, or null if the URL's protocol is not * file */ public static File toFile( final URL url ) { if ( url == null || !url.getProtocol().equalsIgnoreCase( "file" ) ) { return null; } String filename = url.getFile().replace( '/', File.separatorChar ); int pos = -1; while ( ( pos = filename.indexOf( '%', pos + 1 ) ) >= 0 ) { if ( pos + 2 < filename.length() ) { String hexStr = filename.substring( pos + 1, pos + 3 ); char ch = (char) Integer.parseInt( hexStr, 16 ); filename = filename.substring( 0, pos ) + ch + filename.substring( pos + 3 ); } } return new File( filename ); } /** * Convert the array of Files into a list of URLs. * * @param files the array of files * @return the array of URLs * @throws IOException if an error occurs */ public static URL[] toURLs( final File[] files ) throws IOException { final URL[] urls = new URL[files.length]; for ( int i = 0; i < urls.length; i++ ) { urls[i] = files[i].toURI().toURL(); } return urls; } /** * Remove extension from filename. ie * *
     * foo.txt    --> foo
     * a\b\c.jpg  --> a\b\c
     * a\b\c      --> a\b\c
     * 
* * @param filename the path of the file * @return the filename minus extension */ public static String removeExtension( final String filename ) { String ext = extension( filename ); if ( "".equals( ext ) ) { return filename; } final int index = filename.lastIndexOf( ext ) - 1; return filename.substring( 0, index ); } /** * Get extension from filename. ie * *
     * foo.txt    --> "txt"
     * a\b\c.jpg  --> "jpg"
     * a\b\c      --> ""
     * 
* * @param filename the path of the file * @return the extension of filename or "" if none */ public static String getExtension( final String filename ) { return extension( filename ); } /** * Remove path from filename. Equivalent to the unix command basename ie. * *
     * a/b/c.txt --> c.txt
     * a.txt     --> a.txt
     * 
* * @param filepath the path of the file * @return the filename minus path */ public static String removePath( final String filepath ) { return removePath( filepath, File.separatorChar ); } /** * Remove path from filename. ie. * *
     * a/b/c.txt --> c.txt
     * a.txt     --> a.txt
     * 
* * @param filepath the path of the file * @param fileSeparatorChar the file separator character like / on Unix platforms. * @return the filename minus path */ public static String removePath( final String filepath, final char fileSeparatorChar ) { final int index = filepath.lastIndexOf( fileSeparatorChar ); if ( -1 == index ) { return filepath; } return filepath.substring( index + 1 ); } /** * Get path from filename. Roughly equivalent to the unix command dirname. ie. * *
     * a/b/c.txt --> a/b
     * a.txt     --> ""
     * 
* * @param filepath the filepath * @return the filename minus path */ public static String getPath( final String filepath ) { return getPath( filepath, File.separatorChar ); } /** * Get path from filename. ie. * *
     * a/b/c.txt --> a/b
     * a.txt     --> ""
     * 
* * @param filepath the filepath * @param fileSeparatorChar the file separator character like / on Unix platforms. * @return the filename minus path */ public static String getPath( final String filepath, final char fileSeparatorChar ) { final int index = filepath.lastIndexOf( fileSeparatorChar ); if ( -1 == index ) { return ""; } return filepath.substring( 0, index ); } /** * Copy file from source to destination. If destinationDirectory does not exist, it (and any parent * directories) will be created. If a file source in destinationDirectory exists, it will * be overwritten. * * @param source An existing File to copy. * @param destinationDirectory A directory to copy source into. * @throws java.io.FileNotFoundException if source isn't a normal file. * @throws IllegalArgumentException if destinationDirectory isn't a directory. * @throws IOException if source does not exist, the file in destinationDirectory cannot * be written to, or an IO error occurs during copying. */ public static void copyFileToDirectory( final String source, final String destinationDirectory ) throws IOException { copyFileToDirectory( new File( source ), new File( destinationDirectory ) ); } /** * Copy file from source to destination only if source is newer than the target file. If * destinationDirectory does not exist, it (and any parent directories) will be created. If a file * source in destinationDirectory exists, it will be overwritten. * * @param source An existing File to copy. * @param destinationDirectory A directory to copy source into. * @throws java.io.FileNotFoundException if source isn't a normal file. * @throws IllegalArgumentException if destinationDirectory isn't a directory. * @throws IOException if source does not exist, the file in destinationDirectory cannot * be written to, or an IO error occurs during copying. */ public static void copyFileToDirectoryIfModified( final String source, final String destinationDirectory ) throws IOException { copyFileToDirectoryIfModified( new File( source ), new File( destinationDirectory ) ); } /** * Copy file from source to destination. If destinationDirectory does not exist, it (and any parent * directories) will be created. If a file source in destinationDirectory exists, it will * be overwritten. * * @param source An existing File to copy. * @param destinationDirectory A directory to copy source into. * @throws java.io.FileNotFoundException if source isn't a normal file. * @throws IllegalArgumentException if destinationDirectory isn't a directory. * @throws IOException if source does not exist, the file in destinationDirectory cannot * be written to, or an IO error occurs during copying. */ public static void copyFileToDirectory( final File source, final File destinationDirectory ) throws IOException { if ( destinationDirectory.exists() && !destinationDirectory.isDirectory() ) { throw new IllegalArgumentException( "Destination is not a directory" ); } copyFile( source, new File( destinationDirectory, source.getName() ) ); } /** * Copy file from source to destination only if source is newer than the target file. If * destinationDirectory does not exist, it (and any parent directories) will be created. If a file * source in destinationDirectory exists, it will be overwritten. * * @param source An existing File to copy. * @param destinationDirectory A directory to copy source into. * @throws java.io.FileNotFoundException if source isn't a normal file. * @throws IllegalArgumentException if destinationDirectory isn't a directory. * @throws IOException if source does not exist, the file in destinationDirectory cannot * be written to, or an IO error occurs during copying. */ public static void copyFileToDirectoryIfModified( final File source, final File destinationDirectory ) throws IOException { if ( destinationDirectory.exists() && !destinationDirectory.isDirectory() ) { throw new IllegalArgumentException( "Destination is not a directory" ); } copyFileIfModified( source, new File( destinationDirectory, source.getName() ) ); } /** * Creates a number of directories, as delivered from DirectoryScanner * * @param sourceBase The basedir used for the directory scan * @param dirs The getIncludedDirs from the dirscanner * @param destination The base dir of the output structure * @throws IOException io issue */ public static void mkDirs( final File sourceBase, String[] dirs, final File destination ) throws IOException { for ( String dir : dirs ) { File src = new File( sourceBase, dir ); File dst = new File( destination, dir ); if ( NioFiles.isSymbolicLink( src ) ) { File target = NioFiles.readSymbolicLink( src ); NioFiles.createSymbolicLink( dst, target ); } else { dst.mkdirs(); } } } /** * Copy file from source to destination. The directories up to destination will be created if they * don't already exist. destination will be overwritten if it already exists. * * @param source An existing non-directory File to copy bytes from. * @param destination A non-directory File to write bytes to (possibly overwriting). * @throws IOException if source does not exist, destination cannot be written to, or an * IO error occurs during copying. * @throws java.io.FileNotFoundException if destination is a directory (use * {@link #copyFileToDirectory}). */ public static void copyFile( final File source, final File destination ) throws IOException { // check source exists if ( !source.exists() ) { final String message = "File " + source + " does not exist"; throw new IOException( message ); } // check source != destination, see PLXUTILS-10 if ( source.getCanonicalPath().equals( destination.getCanonicalPath() ) ) { // if they are equal, we can exit the method without doing any work return; } mkdirsFor( destination ); doCopyFile( source, destination ); if ( source.length() != destination.length() ) { String message = "Failed to copy full contents from " + source + " to " + destination; throw new IOException( message ); } } private static void doCopyFile( File source, File destination ) throws IOException { doCopyFileUsingNewIO( source, destination ); } private static void doCopyFileUsingNewIO( File source, File destination ) throws IOException { NioFiles.copy( source, destination ); } /** * Link file from destination to source. The directories up to destination will be created if they * don't already exist. destination will be overwritten if it already exists. * * @param source An existing non-directory File to link to. * @param destination A non-directory File becoming the link (possibly overwriting). * @throws IOException if source does not exist, destination cannot be created, or an * IO error occurs during linking. * @throws java.io.FileNotFoundException if destination is a directory (use * {@link #copyFileToDirectory}). */ public static void linkFile( final File source, final File destination ) throws IOException { // check source exists if ( !source.exists() ) { final String message = "File " + source + " does not exist"; throw new IOException( message ); } // check source != destination, see PLXUTILS-10 if ( source.getCanonicalPath().equals( destination.getCanonicalPath() ) ) { // if they are equal, we can exit the method without doing any work return; } mkdirsFor( destination ); NioFiles.createSymbolicLink( destination, source ); } /** * Copy file from source to destination only if source timestamp is later than the destination timestamp. The * directories up to destination will be created if they don't already exist. destination * will be overwritten if it already exists. * * @param source An existing non-directory File to copy bytes from. * @param destination A non-directory File to write bytes to (possibly overwriting). * @return true if no problem occured * @throws IOException if source does not exist, destination cannot be written to, or an * IO error occurs during copying. */ public static boolean copyFileIfModified( final File source, final File destination ) throws IOException { if ( isSourceNewerThanDestination( source, destination ) ) { copyFile( source, destination ); return true; } return false; } /** * Copies bytes from the URL source to a file destination. The directories up to * destination will be created if they don't already exist. destination will be * overwritten if it already exists. * * @param source A URL to copy bytes from. * @param destination A non-directory File to write bytes to (possibly overwriting). * @throws IOException if *
    *
  • source URL cannot be opened
  • *
  • destination cannot be written to
  • *
  • an IO error occurs during copying
  • *
*/ public static void copyURLToFile( final URL source, final File destination ) throws IOException { copyStreamToFile( new URLInputStreamFacade( source ), destination ); } /** * Copies bytes from the {@link InputStream} source to a file destination. The directories * up to destination will be created if they don't already exist. destination will be * overwritten if it already exists. * * @param source An {@link InputStream} to copy bytes from. This stream is guaranteed to be closed. * @param destination A non-directory File to write bytes to (possibly overwriting). * @throws IOException if *
    *
  • source URL cannot be opened
  • *
  • destination cannot be written to
  • *
  • an IO error occurs during copying
  • *
*/ public static void copyStreamToFile( final InputStreamFacade source, final File destination ) throws IOException { mkdirsFor( destination ); checkCanWrite( destination ); try ( InputStream input = source.getInputStream(); OutputStream output = Files.newOutputStream( destination.toPath() ) ) { IOUtil.copy( input, output ); } } private static void checkCanWrite( File destination ) throws IOException { // make sure we can write to destination if ( destination.exists() && !destination.canWrite() ) { final String message = "Unable to open file " + destination + " for writing."; throw new IOException( message ); } } private static void mkdirsFor( File destination ) { // does destination directory exist ? File parentFile = destination.getParentFile(); if ( parentFile != null && !parentFile.exists() ) { parentFile.mkdirs(); } } /** * Normalize a path. Eliminates "/../" and "/./" in a string. Returns null if the ..'s went past the * root. Eg: * *
     * /foo//               -->     /foo/
     * /foo/./              -->     /foo/
     * /foo/../bar          -->     /bar
     * /foo/../bar/         -->     /bar/
     * /foo/../bar/../baz   -->     /baz
     * //foo//./bar         -->     /foo/bar
     * /../                 -->     null
     * 
* * @param path the path to normalize * @return the normalized String, or null if too many ..'s. */ public static String normalize( final String path ) { String normalized = path; // Resolve occurrences of "//" in the normalized path while ( true ) { int index = normalized.indexOf( "//" ); if ( index < 0 ) { break; } normalized = normalized.substring( 0, index ) + normalized.substring( index + 1 ); } // Resolve occurrences of "/./" in the normalized path while ( true ) { int index = normalized.indexOf( "/./" ); if ( index < 0 ) { break; } normalized = normalized.substring( 0, index ) + normalized.substring( index + 2 ); } // Resolve occurrences of "/../" in the normalized path while ( true ) { int index = normalized.indexOf( "/../" ); if ( index < 0 ) { break; } if ( index == 0 ) { return null; // Trying to go outside our context } int index2 = normalized.lastIndexOf( '/', index - 1 ); normalized = normalized.substring( 0, index2 ) + normalized.substring( index + 3 ); } // Return the normalized path that we have completed return normalized; } /** *

Will concatenate 2 paths. Paths with .. will be properly handled.

* * Eg., *
     * /a/b/c + d = /a/b/d
     * /a/b/c + ../d = /a/d
     * 
*

Thieved from Tomcat sources...

* * @param lookupPath a path * @param path the path to concatenate * @return The concatenated paths, or null if error occurs */ public static String catPath( final String lookupPath, final String path ) { // Cut off the last slash and everything beyond int index = lookupPath.lastIndexOf( "/" ); String lookup = lookupPath.substring( 0, index ); String pth = path; // Deal with .. by chopping dirs off the lookup path while ( pth.startsWith( "../" ) ) { if ( lookup.length() > 0 ) { index = lookup.lastIndexOf( "/" ); lookup = lookup.substring( 0, index ); } else { // More ..'s than dirs, return null return null; } index = pth.indexOf( "../" ) + 3; pth = pth.substring( index ); } return new StringBuffer( lookup ).append( "/" ).append( pth ).toString(); } /** * Resolve a file filename to it's canonical form. If filename is relative (doesn't start * with /), it will be resolved relative to baseFile, otherwise it is treated as a normal * root-relative path. * * @param baseFile Where to resolve filename from, if filename is relative. * @param filename Absolute or relative file path to resolve. * @return The canonical File of filename. */ public static File resolveFile( final File baseFile, String filename ) { String filenm = filename; if ( '/' != File.separatorChar ) { filenm = filename.replace( '/', File.separatorChar ); } if ( '\\' != File.separatorChar ) { filenm = filename.replace( '\\', File.separatorChar ); } // deal with absolute files if ( filenm.startsWith( File.separator ) || ( Os.isFamily( Os.FAMILY_WINDOWS ) && filenm.indexOf( ":" ) > 0 ) ) { File file = new File( filenm ); try { file = file.getCanonicalFile(); } catch ( final IOException ioe ) { // nop } return file; } // FIXME: I'm almost certain this // removal is unnecessary, as getAbsoluteFile() strips // them. However, I'm not sure about this UNC stuff. (JT) final char[] chars = filename.toCharArray(); final StringBuilder sb = new StringBuilder(); // remove duplicate file separators in succession - except // on win32 at start of filename as UNC filenames can // be \\AComputer\AShare\myfile.txt int start = 0; if ( '\\' == File.separatorChar ) { sb.append( filenm.charAt( 0 ) ); start++; } for ( int i = start; i < chars.length; i++ ) { final boolean doubleSeparator = File.separatorChar == chars[i] && File.separatorChar == chars[i - 1]; if ( !doubleSeparator ) { sb.append( chars[i] ); } } filenm = sb.toString(); // must be relative File file = ( new File( baseFile, filenm ) ).getAbsoluteFile(); try { file = file.getCanonicalFile(); } catch ( final IOException ioe ) { // nop } return file; } /** * Delete a file. If file is directory delete it and all sub-directories. * * @param file the file path * @throws IOException if any */ public static void forceDelete( final String file ) throws IOException { forceDelete( new File( file ) ); } /** * Delete a file. If file is directory delete it and all sub-directories. * * @param file a file * @throws IOException if any */ public static void forceDelete( final File file ) throws IOException { if ( file.isDirectory() ) { deleteDirectory( file ); } else { /* * NOTE: Always try to delete the file even if it appears to be non-existent. This will ensure that a * symlink whose target does not exist is deleted, too. */ boolean filePresent = file.getCanonicalFile().exists(); if ( !deleteFile( file ) && filePresent ) { final String message = "File " + file + " unable to be deleted."; throw new IOException( message ); } } } /** * Accommodate Windows bug encountered in both Sun and IBM JDKs. Others possible. If the delete does not work, call * System.gc(), wait a little and try again. * * @param file a file * @throws IOException if any */ private static boolean deleteFile( File file ) throws IOException { if ( file.isDirectory() ) { throw new IOException( "File " + file + " isn't a file." ); } if ( !file.delete() ) { if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) { file = file.getCanonicalFile(); System.gc(); } try { Thread.sleep( 10 ); return file.delete(); } catch ( InterruptedException ignore ) { return file.delete(); } } return true; } /** * Schedule a file to be deleted when JVM exits. If file is directory delete it and all sub-directories. * * @param file a file * @throws IOException if any */ public static void forceDeleteOnExit( final File file ) throws IOException { if ( !file.exists() ) { return; } if ( file.isDirectory() ) { deleteDirectoryOnExit( file ); } else { file.deleteOnExit(); } } /** * Recursively schedule directory for deletion on JVM exit. * * @param directory a directory * @throws IOException if any */ private static void deleteDirectoryOnExit( final File directory ) throws IOException { if ( !directory.exists() ) { return; } directory.deleteOnExit(); // The hook reverses the list cleanDirectoryOnExit( directory ); } /** * Clean a directory without deleting it. * * @param directory a directory * @throws IOException if any */ private static void cleanDirectoryOnExit( final File directory ) throws IOException { if ( !directory.exists() ) { final String message = directory + " does not exist"; throw new IllegalArgumentException( message ); } if ( !directory.isDirectory() ) { final String message = directory + " is not a directory"; throw new IllegalArgumentException( message ); } IOException exception = null; final File[] files = directory.listFiles(); for ( final File file : files ) { try { forceDeleteOnExit( file ); } catch ( final IOException ioe ) { exception = ioe; } } if ( null != exception ) { throw exception; } } /** * Make a directory. * * @param file not null * @throws IOException If there already exists a file with specified name or the directory is unable to be created * @throws IllegalArgumentException if the file contains illegal Windows characters under Windows OS. * @see #INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME */ public static void forceMkdir( final File file ) throws IOException { if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) { if ( !isValidWindowsFileName( file ) ) { throw new IllegalArgumentException( "The file (" + file.getAbsolutePath() + ") cannot contain any of the following characters: \n" + StringUtils.join( INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME, " " ) ); } } if ( file.exists() ) { if ( file.isFile() ) { final String message = "File " + file + " exists and is " + "not a directory. Unable to create directory."; throw new IOException( message ); } } else { if ( false == file.mkdirs() ) { final String message = "Unable to create directory " + file; throw new IOException( message ); } } } /** * Recursively delete a directory. * * @param directory a directory * @throws IOException if any */ public static void deleteDirectory( final String directory ) throws IOException { deleteDirectory( new File( directory ) ); } /** * Recursively delete a directory. * * @param directory a directory * @throws IOException if any */ public static void deleteDirectory( final File directory ) throws IOException { if ( !directory.exists() ) { return; } /* * try delete the directory before its contents, which will take care of any directories that are really * symbolic links. */ if ( directory.delete() ) { return; } cleanDirectory( directory ); if ( !directory.delete() ) { final String message = "Directory " + directory + " unable to be deleted."; throw new IOException( message ); } } /** * Clean a directory without deleting it. * * @param directory a directory * @throws IOException if any */ public static void cleanDirectory( final String directory ) throws IOException { cleanDirectory( new File( directory ) ); } /** * Clean a directory without deleting it. * * @param directory a directory * @throws IOException if any */ public static void cleanDirectory( final File directory ) throws IOException { if ( !directory.exists() ) { final String message = directory + " does not exist"; throw new IllegalArgumentException( message ); } if ( !directory.isDirectory() ) { final String message = directory + " is not a directory"; throw new IllegalArgumentException( message ); } IOException exception = null; final File[] files = directory.listFiles(); if ( files == null ) { return; } for ( final File file : files ) { try { forceDelete( file ); } catch ( final IOException ioe ) { exception = ioe; } } if ( null != exception ) { throw exception; } } /** * Recursively count size of a directory. * * @param directory a directory * @return size of directory in bytes. */ public static long sizeOfDirectory( final String directory ) { return sizeOfDirectory( new File( directory ) ); } /** * Recursively count size of a directory. * * @param directory a directory * @return size of directory in bytes. */ public static long sizeOfDirectory( final File directory ) { if ( !directory.exists() ) { final String message = directory + " does not exist"; throw new IllegalArgumentException( message ); } if ( !directory.isDirectory() ) { final String message = directory + " is not a directory"; throw new IllegalArgumentException( message ); } long size = 0; final File[] files = directory.listFiles(); for ( final File file : files ) { if ( file.isDirectory() ) { size += sizeOfDirectory( file ); } else { size += file.length(); } } return size; } /** * Return the files contained in the directory, using inclusion and exclusion Ant patterns, including the directory * name in each of the files * * @param directory the directory to scan * @param includes the includes pattern, comma separated * @param excludes the excludes pattern, comma separated * @return a list of File objects * @throws IOException io issue * @see #getFileNames(File, String, String, boolean) */ public static List getFiles( File directory, String includes, String excludes ) throws IOException { return getFiles( directory, includes, excludes, true ); } /** * Return the files contained in the directory, using inclusion and exclusion Ant patterns * * @param directory the directory to scan * @param includes the includes pattern, comma separated * @param excludes the excludes pattern, comma separated * @param includeBasedir true to include the base dir in each file * @return a list of File objects * @throws IOException io issue * @see #getFileNames(File, String, String, boolean) */ public static List getFiles( File directory, String includes, String excludes, boolean includeBasedir ) throws IOException { List fileNames = getFileNames( directory, includes, excludes, includeBasedir ); List files = new ArrayList(); for ( String filename : fileNames ) { files.add( new File( filename ) ); } return files; } /** * Return a list of files as String depending options. This method use case sensitive file name. * * @param directory the directory to scan * @param includes the includes pattern, comma separated * @param excludes the excludes pattern, comma separated * @param includeBasedir true to include the base dir in each String of file * @return a list of files as String * @throws IOException io issue */ public static List getFileNames( File directory, String includes, String excludes, boolean includeBasedir ) throws IOException { return getFileNames( directory, includes, excludes, includeBasedir, true ); } /** * Return a list of files as String depending options. * * @param directory the directory to scan * @param includes the includes pattern, comma separated * @param excludes the excludes pattern, comma separated * @param includeBasedir true to include the base dir in each String of file * @param isCaseSensitive true if case sensitive * @return a list of files as String * @throws IOException io issue */ public static List getFileNames( File directory, String includes, String excludes, boolean includeBasedir, boolean isCaseSensitive ) throws IOException { return getFileAndDirectoryNames( directory, includes, excludes, includeBasedir, isCaseSensitive, true, false ); } /** * Return a list of directories as String depending options. This method use case sensitive file name. * * @param directory the directory to scan * @param includes the includes pattern, comma separated * @param excludes the excludes pattern, comma separated * @param includeBasedir true to include the base dir in each String of file * @return a list of directories as String * @throws IOException io issue */ public static List getDirectoryNames( File directory, String includes, String excludes, boolean includeBasedir ) throws IOException { return getDirectoryNames( directory, includes, excludes, includeBasedir, true ); } /** * Return a list of directories as String depending options. * * @param directory the directory to scan * @param includes the includes pattern, comma separated * @param excludes the excludes pattern, comma separated * @param includeBasedir true to include the base dir in each String of file * @param isCaseSensitive true if case sensitive * @return a list of directories as String * @throws IOException io issue */ public static List getDirectoryNames( File directory, String includes, String excludes, boolean includeBasedir, boolean isCaseSensitive ) throws IOException { return getFileAndDirectoryNames( directory, includes, excludes, includeBasedir, isCaseSensitive, false, true ); } /** * Return a list of files as String depending options. * * @param directory the directory to scan * @param includes the includes pattern, comma separated * @param excludes the excludes pattern, comma separated * @param includeBasedir true to include the base dir in each String of file * @param isCaseSensitive true if case sensitive * @param getFiles true if get files * @param getDirectories true if get directories * @return a list of files as String * @throws IOException io issue */ public static List getFileAndDirectoryNames( File directory, String includes, String excludes, boolean includeBasedir, boolean isCaseSensitive, boolean getFiles, boolean getDirectories ) throws IOException { DirectoryScanner scanner = new DirectoryScanner(); scanner.setBasedir( directory ); if ( includes != null ) { scanner.setIncludes( StringUtils.split( includes, "," ) ); } if ( excludes != null ) { scanner.setExcludes( StringUtils.split( excludes, "," ) ); } scanner.setCaseSensitive( isCaseSensitive ); scanner.scan(); List list = new ArrayList(); if ( getFiles ) { String[] files = scanner.getIncludedFiles(); for ( String file : files ) { if ( includeBasedir ) { list.add( directory + FileUtils.FS + file ); } else { list.add( file ); } } } if ( getDirectories ) { String[] directories = scanner.getIncludedDirectories(); for ( String directory1 : directories ) { if ( includeBasedir ) { list.add( directory + FileUtils.FS + directory1 ); } else { list.add( directory1 ); } } } return list; } /** * Copy a directory to an other one. * * @param sourceDirectory the source dir * @param destinationDirectory the target dir * @throws IOException if any */ public static void copyDirectory( File sourceDirectory, File destinationDirectory ) throws IOException { copyDirectory( sourceDirectory, destinationDirectory, "**", null ); } /** * Copy a directory to an other one. * * @param sourceDirectory the source dir * @param destinationDirectory the target dir * @param includes include pattern * @param excludes exclude pattern * @throws IOException if any * @see #getFiles(File, String, String) */ public static void copyDirectory( File sourceDirectory, File destinationDirectory, String includes, String excludes ) throws IOException { if ( !sourceDirectory.exists() ) { return; } List files = getFiles( sourceDirectory, includes, excludes ); for ( File file : files ) { copyFileToDirectory( file, destinationDirectory ); } } /** *

Copies a entire directory layout : no files will be copied only directories

* * Note: *
    *
  • It will include empty directories. *
  • The sourceDirectory must exists. *
* * @param sourceDirectory the source dir * @param destinationDirectory the target dir * @param includes include pattern * @param excludes exclude pattern * @throws IOException if any * @since 1.5.7 */ public static void copyDirectoryLayout( File sourceDirectory, File destinationDirectory, String[] includes, String[] excludes ) throws IOException { if ( sourceDirectory == null ) { throw new IOException( "source directory can't be null." ); } if ( destinationDirectory == null ) { throw new IOException( "destination directory can't be null." ); } if ( sourceDirectory.equals( destinationDirectory ) ) { throw new IOException( "source and destination are the same directory." ); } if ( !sourceDirectory.exists() ) { throw new IOException( "Source directory doesn't exists (" + sourceDirectory.getAbsolutePath() + ")." ); } DirectoryScanner scanner = new DirectoryScanner(); scanner.setBasedir( sourceDirectory ); if ( includes != null && includes.length >= 1 ) { scanner.setIncludes( includes ); } else { scanner.setIncludes( new String[] { "**" } ); } if ( excludes != null && excludes.length >= 1 ) { scanner.setExcludes( excludes ); } scanner.addDefaultExcludes(); scanner.scan(); List includedDirectories = Arrays.asList( scanner.getIncludedDirectories() ); for ( String name : includedDirectories ) { File source = new File( sourceDirectory, name ); if ( source.equals( sourceDirectory ) ) { continue; } File destination = new File( destinationDirectory, name ); destination.mkdirs(); } } /** *

Copies a entire directory structure.

* * Note: *
    *
  • It will include empty directories. *
  • The sourceDirectory must exists. *
* * @param sourceDirectory the source dir * @param destinationDirectory the target dir * @throws IOException if any */ public static void copyDirectoryStructure( File sourceDirectory, File destinationDirectory ) throws IOException { copyDirectoryStructure( sourceDirectory, destinationDirectory, destinationDirectory, false ); } /** *

Copies an entire directory structure but only source files with timestamp later than the destinations'.

* * Note: *
    *
  • It will include empty directories. *
  • The sourceDirectory must exists. *
* * @param sourceDirectory the source dir * @param destinationDirectory the target dir * @throws IOException if any */ public static void copyDirectoryStructureIfModified( File sourceDirectory, File destinationDirectory ) throws IOException { copyDirectoryStructure( sourceDirectory, destinationDirectory, destinationDirectory, true ); } private static void copyDirectoryStructure( File sourceDirectory, File destinationDirectory, File rootDestinationDirectory, boolean onlyModifiedFiles ) throws IOException { if ( sourceDirectory == null ) { throw new IOException( "source directory can't be null." ); } if ( destinationDirectory == null ) { throw new IOException( "destination directory can't be null." ); } if ( sourceDirectory.equals( destinationDirectory ) ) { throw new IOException( "source and destination are the same directory." ); } if ( !sourceDirectory.exists() ) { throw new IOException( "Source directory doesn't exists (" + sourceDirectory.getAbsolutePath() + ")." ); } File[] files = sourceDirectory.listFiles(); String sourcePath = sourceDirectory.getAbsolutePath(); for ( File file : files ) { if ( file.equals( rootDestinationDirectory ) ) { // We don't copy the destination directory in itself continue; } String dest = file.getAbsolutePath(); dest = dest.substring( sourcePath.length() + 1 ); File destination = new File( destinationDirectory, dest ); if ( file.isFile() ) { destination = destination.getParentFile(); if ( onlyModifiedFiles ) { copyFileToDirectoryIfModified( file, destination ); } else { copyFileToDirectory( file, destination ); } } else if ( file.isDirectory() ) { if ( !destination.exists() && !destination.mkdirs() ) { throw new IOException( "Could not create destination directory '" + destination.getAbsolutePath() + "'." ); } copyDirectoryStructure( file, destination, rootDestinationDirectory, onlyModifiedFiles ); } else { throw new IOException( "Unknown file type: " + file.getAbsolutePath() ); } } } /** *

Renames a file, even if that involves crossing file system boundaries.

* *

This will remove to (if it exists), ensure that to's parent directory exists and move * from, which involves deleting from as well.

* * @param from the file to move * @param to the new file name * @throws IOException if anything bad happens during this process. Note that to may have been deleted * already when this happens. */ public static void rename( File from, File to ) throws IOException { if ( to.exists() && !to.delete() ) { throw new IOException( "Failed to delete " + to + " while trying to rename " + from ); } File parent = to.getParentFile(); if ( parent != null && !parent.exists() && !parent.mkdirs() ) { throw new IOException( "Failed to create directory " + parent + " while trying to rename " + from ); } if ( !from.renameTo( to ) ) { copyFile( from, to ); if ( !from.delete() ) { throw new IOException( "Failed to delete " + from + " while trying to rename it." ); } } } /** *

Create a temporary file in a given directory.

* *

The file denoted by the returned abstract pathname did not exist before this method was invoked, any subsequent * invocation of this method will yield a different file name.

* *

The filename is prefixNNNNNsuffix where NNNN is a random number

* *

This method is different to {@link File#createTempFile(String, String, File)} of JDK 1.2 as it doesn't create the * file itself. It uses the location pointed to by java.io.tmpdir when the parentDir attribute is null.

* *

To delete automatically the file created by this method, use the {@link File#deleteOnExit()} method.

* * @param prefix prefix before the random number * @param suffix file extension; include the '.' * @param parentDir Directory to create the temporary file in -java.io.tmpdir used if not specificed * @return a File reference to the new temporary file. */ public static File createTempFile( String prefix, String suffix, File parentDir ) { File result = null; String parent = System.getProperty( "java.io.tmpdir" ); if ( parentDir != null ) { parent = parentDir.getPath(); } DecimalFormat fmt = new DecimalFormat( "#####" ); SecureRandom secureRandom = new SecureRandom(); long secureInitializer = secureRandom.nextLong(); Random rand = new Random( secureInitializer + Runtime.getRuntime().freeMemory() ); synchronized ( rand ) { do { result = new File( parent, prefix + fmt.format( Math.abs( rand.nextInt() ) ) + suffix ); } while ( result.exists() ); } return result; } /** * If wrappers is null or empty, the file will be copy only if {@code to.lastModified() < from.lastModified()} * * @param from the file to copy * @param to the destination file * @param encoding the file output encoding (only if wrappers is not empty) * @param wrappers array of {@link FilterWrapper} * @throws IOException if an IO error occurs during copying or filtering */ public static void copyFile( File from, File to, String encoding, FilterWrapper[] wrappers ) throws IOException { copyFile( from, to, encoding, wrappers, false ); } public static abstract class FilterWrapper { public abstract Reader getReader( Reader fileReader ); } /** * If wrappers is null or empty, the file will be copy only if {@code to.lastModified() < from.lastModified()}, if overwrite is true * * @param from the file to copy * @param to the destination file * @param encoding the file output encoding (only if wrappers is not empty) * @param wrappers array of {@link FilterWrapper} * @param overwrite if true and wrappers is null or empty, the file will be copied even if {@code to.lastModified() < from.lastModified()} * @throws IOException if an IO error occurs during copying or filtering * @since 1.5.2 */ public static void copyFile( File from, File to, String encoding, FilterWrapper[] wrappers, boolean overwrite ) throws IOException { if ( wrappers != null && wrappers.length > 0 ) { // buffer so it isn't reading a byte at a time! Reader fileReader = null; Writer fileWriter = null; try { if ( encoding == null || encoding.length() < 1 ) { fileReader = Files.newBufferedReader( from.toPath() ); fileWriter = Files.newBufferedWriter( to.toPath() ); } else { OutputStream outstream = Files.newOutputStream( to.toPath() ); fileReader = Files.newBufferedReader( from.toPath(), Charset.forName( encoding ) ); fileWriter = new OutputStreamWriter( outstream, encoding ); } Reader reader = fileReader; for ( FilterWrapper wrapper : wrappers ) { reader = wrapper.getReader( reader ); } IOUtil.copy( reader, fileWriter ); fileWriter.close(); fileWriter = null; fileReader.close(); fileReader = null; } finally { IOUtil.close( fileReader ); IOUtil.close( fileWriter ); } } else { if ( isSourceNewerThanDestination( from, to ) || overwrite ) { copyFile( from, to ); } } } private static boolean isSourceNewerThanDestination( File source, File destination ) { return ( destination.lastModified() == 0L && source.lastModified() == 0L ) || destination.lastModified() < source.lastModified(); } /** * Note: the file content is read with platform encoding * * @param file the file * @return a List containing every every line not starting with # and not empty * @throws IOException if any */ public static List loadFile( File file ) throws IOException { final List lines = new ArrayList(); if ( file.exists() ) { try ( BufferedReader reader = Files.newBufferedReader( file.toPath() ) ) { for ( String line = reader.readLine(); line != null; line = reader.readLine() ) { line = line.trim(); if ( !line.startsWith( "#" ) && line.length() != 0 ) { lines.add( line ); } } } } return lines; } /** * For Windows OS, check if the file name contains any of the following characters: * ":", "*", "?", "\"", "<", ">", "|" * * @param f not null file * @return false if the file path contains any of forbidden Windows characters, true if * the Os is not Windows or if the file path respect the Windows constraints. * @see #INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME * @since 1.5.2 */ public static boolean isValidWindowsFileName( File f ) { if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) { if ( StringUtils.indexOfAny( f.getName(), INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME ) != -1 ) { return false; } File parentFile = f.getParentFile(); if ( parentFile != null ) { return isValidWindowsFileName( parentFile ); } } return true; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy