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

org.aksw.commons.io.util.FileUtils Maven / Gradle / Ivy

package org.aksw.commons.io.util;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.nio.channels.FileChannel;
import java.nio.file.AtomicMoveNotSupportedException;
import java.nio.file.DirectoryStream;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Stream;

import com.google.common.collect.Lists;
import com.google.common.io.MoreFiles;
import com.google.common.io.RecursiveDeleteOption;

public class FileUtils {

    public static void deleteRecursivelyIfExists(Path path, RecursiveDeleteOption ... options) throws IOException {
        if (Files.exists(path)) {
            MoreFiles.deleteRecursively(path, options);
        }
    }

    /** Return the first ancestor of a path that exists.
     *  May be the path itself or one if its transitive parents.
     *  Returns null on null input.
     *  Use {@link Path#relativize(Path)} to obtain the folders that would
     *  have to be created.
     */
    public static Path getFirstExistingAncestor(Path path) {
        Path result = path == null
                    ? null
                    : Files.exists(path)
                        ? path
                        : getFirstExistingAncestor(path.getParent());
        return result;
    }

    /**
     * Delete a specific path and then - regardless of deletion outcome -try to delete all empty directories up to a given baseFolder.
     * Empty folders are only deleted if their path starts with the baseFolder
     *
     * The result is the same as of {@link Files#deleteIfExists(Path)}.
     */
    public static boolean deleteFileIfExistsAndThenDeleteEmptyFolders(Path path, Path baseFolder, boolean alsoDeleteBaseFolder) throws IOException {
        boolean result = Files.deleteIfExists(path);
        path = path.getParent();
        if (path != null) {
            deleteEmptyFolders(path, baseFolder, alsoDeleteBaseFolder);
        }

        return result;
    }

    /** Delete parent folders of 'path' that are descendants of baseFolder (depending on the flag inclusive or exclusive) */
    public static void deleteEmptyFolders(Path path, Path baseFolder, boolean alsoDeleteBase) {
        while (path.startsWith(baseFolder) && (alsoDeleteBase || !path.equals(baseFolder))) {
            if (Files.exists(path)) {
                if (Files.isDirectory(path)) {
                    // There can be race conditions when threads concurrently create files
                    // and attempt to clean empty dirs - it seems its better to silently ignore errors
                    // throw new IllegalArgumentException("Path must be a directory: " + path);

                    try {
                        Files.deleteIfExists(path);
                    } catch (IOException e) {
                        // Ignore
                        break;
                    }
                }
            }
            path = path.getParent();
        }
    }

    /** Best-effort moveAtomic */
    public static void moveAtomic(Path srcFile, Path tgtPath) throws IOException {
        try {
            Files.move(srcFile, tgtPath, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
        } catch (AtomicMoveNotSupportedException e) {
            try (
                FileChannel srcChannel = FileChannel.open(srcFile, StandardOpenOption.READ);
                FileChannel tgtChannel = FileChannel.open(tgtPath, StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {
                long n = srcChannel.size();
                FileChannelUtils.transferFromFully(tgtChannel, srcChannel, 0, n, null);
                tgtChannel.force(true);
            }

            Files.delete(srcFile);
        }
    }


    /** Delete a path if it is an empty directory */
    public void deleteDirectoryIfEmpty(Path path) throws IOException {
        boolean isDirectory = Files.isDirectory(path);
        if (isDirectory) {
            boolean isEmpty = Files.list(path).count() == 0;
            if (isEmpty) {
                Files.delete(path);
            }
        }
    }

    /** TODO Add: Recursively delete all empty folders
     * @throws IOException */



    /**
     * Return a list of files matching a given glob pattern and a base path
     *
     * @param basePath
     * @param glob
     * @return
     * @throws IOException
     */
    public static List listPaths(Path basePath, String glob) throws IOException {
        List result = null;
        try(DirectoryStream stream = Files.newDirectoryStream(basePath, glob)) {
            result = Lists.newArrayList(stream.iterator());
        }
        return result;
    }

    @SuppressWarnings("unchecked")
    public static  T readObject(Path target) throws IOException, ClassNotFoundException {
        Object obj;
        try (ObjectInputStream in = new ObjectInputStream(Files.newInputStream(target, StandardOpenOption.READ))) {
            obj = in.readObject();
        }

        return (T)obj;
    }


    /** Most basic (and limited) serialization approach using ObjectOutputStream */
    public static void writeObject(Path target, Object obj) throws IOException {
        try (ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(target, StandardOpenOption.WRITE, StandardOpenOption.CREATE))) {
            out.writeObject(obj);
            out.flush();
        }
    }


    /**
     * A java.nio variant of apache commons-io FileUtils.sizeOfDirectory.
     *
     * @param dirPath
     * @return
     * @throws IOException
     */
    public static long sizeOfDirectory(Path dirPath) throws IOException {
        return sizeOfDirectory(dirPath, null);
    }


    /**
     * A java.nio variant of apache commons-io FileUtils.sizeOfDirectory.
     *
     * @param dirPath
     * @param fileMatcher Predicate to test whether to take a certain file's size into account for the total sum
     *   null will match any file.
     * @return
     * @throws IOException
     */
    public static long sizeOfDirectory(Path dirPath, PathMatcher fileMatcher) throws IOException {
        final AtomicLong totalSize = new AtomicLong(0L);

        Files.walkFileTree(dirPath, new SimpleFileVisitor() {
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                boolean isAccepted = fileMatcher == null || fileMatcher.matches(file);
                if (isAccepted) {
                    long contrib = attrs.size();
                    // System.out.println("Path: " + file + " size: " + contrib);
                    totalSize.addAndGet(contrib);
                }
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
                return FileVisitResult.CONTINUE;
            }
        });

        return totalSize.get();
    }

    public static Stream ancestors(Path start, boolean reflexive) {
        Stream r = ancestors(start);
        if (!reflexive) {
            r = r.skip(1);
        }
        return r;
    }

    public static Stream ancestors(Path start) {
        return Stream.iterate(start, node -> node.getParent() != null, Path::getParent);
    }

    /**
     * Look for a file among all ancestor folders
     *
     * @param start
     * @param fileName
     * @return
     */
    public static Path findInAncestors(Path start, String fileName) {
        return ancestors(start)
            .filter(folder -> Files.exists(folder.resolve(fileName)))
            .map(folder -> folder.resolve(fileName))
            .findFirst()
            .orElse(null);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy