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

org.openl.util.FileUtils Maven / Gradle / Ivy

The newest version!
package org.openl.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Collection;

import org.slf4j.LoggerFactory;

/**
 * A set of methods to work with a file system.
 *
 * @author Yury Molchan
 */
public class FileUtils {

    private static final int DEFAULT_BUFFER_SIZE = 8 * 1024 * 1024;

    /**
     * Returns the path to the system temporary directory.
     *
     * @return the path to the system temporary directory.
     */
    public static String getTempDirectoryPath() {
        return System.getProperty("java.io.tmpdir");
    }

    /**
     * Copies a file to a new location preserving the file date.
     * 

* This method copies the contents of the specified source file to the specified destination file. The directory * holding the destination file is created if it does not exist. If the destination file exists, then this method * will overwrite it. *

* Note: This method tries to preserve the file's last modified date/times using * {@link File#setLastModified(long)}, however it is not guaranteed that the operation will succeed. If the * modification operation fails, no indication is provided. * * @param src an existing file to copy, must not be {@code null} * @param dest the new file, must not be {@code null} * @throws NullPointerException if source or destination is {@code null} * @throws IOException if source or destination is invalid * @throws IOException if an IO error occurs during copying */ public static void copy(File src, File dest) throws IOException { if (!src.exists()) { throw new FileNotFoundException(String.format("Source '%s' does not exist", src)); } final String srcPath = src.getCanonicalPath(); final String destPath = dest.getCanonicalPath(); if (srcPath.equals(destPath)) { throw new IOException(String.format("Source '%s' and destination '%s' are the same", src, dest)); } if (src.isDirectory()) { Collection looped = getLoopedDirectories(src, dest); doCopyDirectory(src, dest, looped); } else { if (destPath.startsWith(srcPath)) { throw new IOException( String.format("Destination '%s' has the same path of the source '%s'", dest, src)); } File destFile = dest; if (dest.isDirectory()) { destFile = new File(dest, src.getName()); } else { File parentFile = dest.getParentFile(); if (parentFile != null && !parentFile.mkdirs() && !parentFile.isDirectory()) { throw new IOException(String.format("Destination '%s' directory cannot be created", parentFile)); } } doCopyFile(src, destFile); } } /** * Collects nested directories which should be excluded for copying to prevent an infinity loop of copying. * * @param src the source directory * @param dest the destination directory * @return the list of looped directories * @throws IOException if an I/O error occurs */ private static Collection getLoopedDirectories(File src, File dest) throws IOException { if (!dest.getCanonicalPath().startsWith(src.getCanonicalPath())) { return null; } Collection looped = null; File[] srcFiles = src.listFiles(); if (srcFiles != null && srcFiles.length > 0) { looped = new ArrayList<>(srcFiles.length + 1); for (File srcFile : srcFiles) { File copiedFile = new File(dest, srcFile.getName()); if (srcFile.isDirectory()) { looped.add(copiedFile.getCanonicalPath()); } } if (!dest.exists()) { looped.add(dest.getCanonicalPath()); } } return looped; } /** * Internal copy directory method. * * @param srcDir the validated source directory, must not be {@code null} * @param destDir the validated destination directory, must not be {@code null} * @param excluded the list of directories or files to exclude from the copy, may be null * @throws IOException if an error occurs */ private static void doCopyDirectory(File srcDir, File destDir, Collection excluded) throws IOException { File[] srcFiles = srcDir.listFiles(); if (srcFiles == null) { // null if security restricted throw new IOException("Failed to list contents of " + srcDir); } if (destDir.exists()) { if (!destDir.isDirectory()) { throw new IOException(String.format("Destination '%s' exists but is not a directory", destDir)); } } else { if (!destDir.mkdirs() && !destDir.isDirectory()) { throw new IOException(String.format("Destination '%s' directory cannot be created", destDir)); } } // recurse copying for (File srcFile : srcFiles) { File dstFile = new File(destDir, srcFile.getName()); if (excluded == null || !excluded.contains(srcFile.getCanonicalPath())) { if (srcFile.isDirectory()) { doCopyDirectory(srcFile, dstFile, excluded); } else { doCopyFile(srcFile, dstFile); } } } // Try to preserve file date if (!destDir.setLastModified(srcDir.lastModified())) { LoggerFactory.getLogger(FileUtils.class).warn("Failed to set modified time to file '{}'.", destDir); } } /** * Internal copy file method. * * @param srcFile the validated source file, must not be {@code null} * @param destFile the validated destination file, must not be {@code null} * @throws IOException if an error occurs */ private static void doCopyFile(File srcFile, File destFile) throws IOException { if (destFile.exists() && destFile.isDirectory()) { throw new IOException(String.format("Destination '%s' exists but is a directory", destFile)); } FileInputStream fis = null; FileOutputStream fos = null; FileChannel input = null; FileChannel output = null; try { fis = new FileInputStream(srcFile); fos = new FileOutputStream(destFile); input = fis.getChannel(); output = fos.getChannel(); long size = input.size(); long pos = 0; while (pos < size) { pos += output.transferFrom(input, pos, DEFAULT_BUFFER_SIZE); } } finally { IOUtils.closeQuietly(output); IOUtils.closeQuietly(fos); IOUtils.closeQuietly(input); IOUtils.closeQuietly(fis); } if (srcFile.length() != destFile.length()) { throw new IOException(String.format("Failed to copy full contents from '%s' to '%s'", srcFile, destFile)); } // Try to preserve file date if (!destFile.setLastModified(srcFile.lastModified())) { LoggerFactory.getLogger(FileUtils.class).warn("Failed to set modified time to file '{}'.", destFile); } } /** * Moves a directory or a file. *

* When the destination directory or file is on another file system, do a "copy and delete". * * @param src the directory or the file to be moved * @param dest the destination directory or file * @throws NullPointerException if source or destination is {@code null} * @throws IOException if source or destination is invalid * @throws IOException if an IO error occurs moving the file */ public static void move(File src, File dest) throws IOException { if (!src.exists()) { throw new FileNotFoundException(String.format("Source '%s' does not exist", src)); } if (dest.exists()) { throw new IOException(String.format("Destination '%s' already exists", dest)); } boolean rename = src.renameTo(dest); if (!rename) { if (src.isDirectory() && dest.getCanonicalPath().startsWith(src.getCanonicalPath())) { throw new IOException( String.format("Cannot move directory '%s' to a subdirectory of itself '%s'.", src, dest)); } copy(src, dest); delete(src); if (src.exists()) { throw new IOException( String.format("Failed to delete original directory or file '%s' after copy to '%s'", src, dest)); } } } /** * Deletes a file. If file is a directory, delete it and all sub-directories. *

* The difference between File.delete() and this method are: *

    *
  • A directory to be deleted does not have to be empty.
  • *
  • You get exceptions when a file or directory cannot be deleted.
  • *
* * @param file file or directory to delete, must not be {@code null} * @throws NullPointerException if the directory is {@code null} * @throws FileNotFoundException if the file has not been found * @throws IOException in case deletion is unsuccessful */ public static void delete(File file) throws IOException { if (file.isDirectory()) { File[] files = file.listFiles(); if (files == null) { // null if security restricted throw new IOException("Failed to list contents of directory: " + file); } IOException exception = null; for (File fl : files) { try { delete(fl); } catch (IOException ioe) { exception = ioe; } } if (null != exception) { throw exception; } if (!file.delete()) { throw new IOException("Unable to delete directory: " + file); } } else { boolean filePresent = file.exists(); if (!file.delete()) { if (!filePresent) { throw new FileNotFoundException("File does not exist: " + file); } throw new IOException("Unable to delete file: " + file); } } } /** * Deletes a path. If provided path is a directory, delete it and all sub-directories. * * @param root path to file or directory to delete, must not be {@code null} * @throws NullPointerException if the directory is {@code null} * @throws FileNotFoundException if the file has not been found * @throws IOException in case deletion is unsuccessful */ public static void delete(Path root) throws IOException { if (!Files.exists(root)) { throw new FileNotFoundException("Path does not exist: " + root); } Files.walkFileTree(root, new SimpleFileVisitor<>() { public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { delete0(file); return FileVisitResult.CONTINUE; } public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { delete0(dir); return FileVisitResult.CONTINUE; } }); } /** * Delete file or directory using old API. Because {@link Files#delete(Path)} throws sometimes * {@link java.nio.file.AccessDeniedException} by unknown reason on Windows environment * * @param path path to delete * @throws IOException if failed to delete */ private static void delete0(Path path) throws IOException { File toDelete = path.toFile(); if (!toDelete.delete()) { throw new IOException("Failed to delete: " + path); } } /** * Deletes a file, never throwing an exception. If file is a directory, delete it and all sub-directories. *

* The difference between File.delete() and this method are: *

    *
  • A directory to be deleted does not have to be empty.
  • *
  • No exceptions are thrown when a file or directory cannot be deleted.
  • *
* * @param file file or directory to delete, can be {@code null} */ public static void deleteQuietly(File file) { if (file == null) { return; } try { delete(file); } catch (Exception ignored) { // ignore } } public static void deleteQuietly(Path path) { if (path == null) { return; } try { delete(path); } catch (Exception ignored) { // ignore } } /** * Gets the name minus the path from a full filename. *

* This method will handle a file in either Unix or Windows format. The text after the last forward or backslash is * returned. * *

     * a/b/c.txt --> c.txt
     * a.txt     --> a.txt
     * a/b/c     --> c
     * a/b/c/    --> ""
     * 
*

* * @param filename the filename to query, null returns null * @return the name of the file without the path, or an empty string if none exists */ public static String getName(String filename) { if (filename == null) { return null; } int sep = getSeparatorIndex(filename); return filename.substring(sep + 1); } /** * Gets the base name, minus the full path and extension, from a full filename. *

* This method will handle a file in either Unix or Windows format. The text after the last forward or backslash and * before the last dot is returned. * *

     * a/b/c.txt --> c
     * a.b.txt   --> a.b
     * a/b/c     --> c
     * a/b/c/    --> ""
     * 
*

* * @param filename the filename to query, null returns null * @return the name of the file without the path, or an empty string if none exists */ public static String getBaseName(String filename) { if (filename == null) { return null; } int dot = filename.lastIndexOf('.'); int sep = getSeparatorIndex(filename); if (dot > sep) { return filename.substring(sep + 1, dot); } else { return filename.substring(sep + 1); } } /** * Gets the extension of a filename. *

* This method returns the textual part of the filename after the last dot. There must be no directory separator * after the dot. * *

     * a/b/c.txt    --> txt
     * a.b.txt      --> txt
     * a/b.txt/c    --> ""
     * a/b/c        --> ""
     * 
*

* * @param filename the filename to retrieve the extension of. * @return the extension of the file or an empty string if none exists or {@code null} if the filename is * {@code null}. */ public static String getExtension(String filename) { if (filename == null) { return null; } int dot = getExtensionIndex(filename); if (dot == -1) { return StringUtils.EMPTY; } else { return filename.substring(dot + 1); } } /** * Removes the extension from a filename. *

* This method returns the textual part of the filename before the last dot. There must be no directory separator * after the dot. * *

     * foo.txt    --> foo
     * a\b\c.jpg  --> a\b\c
     * a\b\c      --> a\b\c
     * a.b\c      --> a.b\c
     * 
*

* * @param filename the filename to query, null returns null * @return the filename minus the extension */ public static String removeExtension(String filename) { if (filename == null) { return null; } int dot = getExtensionIndex(filename); if (dot == -1) { return filename; } else { return filename.substring(0, dot); } } private static int getSeparatorIndex(String filename) { int winSep = filename.lastIndexOf('\\'); int unixSep = filename.lastIndexOf('/'); return Math.max(winSep, unixSep); } private static int getExtensionIndex(String filename) { int dot = filename.lastIndexOf('.'); if (dot == -1) { return -1; } int sep = getSeparatorIndex(filename); if (dot > sep) { return dot; } return -1; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy