Package eu.bges

Source Code of eu.bges.IOUtil

/*
* Copyright 2014 Matthias Braun, Martin Gangl
*
* This library is free software: you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.bges;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Serializable;
import java.net.URI;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.file.DirectoryStream;
import java.nio.file.FileSystems;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang3.SerializationUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Charsets;
import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import com.google.common.base.StandardSystemProperty;
import com.google.common.io.Files;

import eu.bges.config.KeyNotFoundException;

/**
* Constants and functions for dealing with files and file systems.
*
* @author Matthias Braun
* @author Martin Gangl
*/
public final class IOUtil {

    private static final Logger LOG = LoggerFactory.getLogger(IOUtil.class);

    /**
     * A regular expression matching every string.
     */
    private static final String MATCH_ALL = ".*";

    /**
     * Error message for dealing with IO exceptions. The braces are used by <a
     * href="http://www.slf4j.org/">SLF4J</a> to insert the file or directory
     * name.
     */
    private static final String COULD_NOT_READ_FILE = "IOException while reading {}",
            COULD_NOT_OPEN_DIR = "IOException opening directory {}",
            COULD_NOT_PARSE_FILE = "Could not parse file {}",
            COULD_NOT_WRITE_TO_FILE = "Could not write to file {}";

    /**
     * The separator between directories is the forward slash (also for
     * Windows).
     */
    public static final String DIR_SEP = "/";

    /**
     * Separates lines in files. On UNIX systems, this is "\n"; on Microsoft
     * Windows systems this is "\r\n".
     */
    public static final String EOL = System.lineSeparator();
    /**
     * The Eight-bit UCS Transformation Format.
     */
    public static final Charset UTF_8 = Charset.forName("UTF-8");

    /** This is a utility class not meant to be instantiated by others. */
    private IOUtil() {
    }

    /**
     * Appends text to a file. Creates the file and the needed directories if
     * they do not exist.
     * <p>
     * UTF-8 is used for encoding the file's content.
     *
     * @param txt
     *            text appended to the file
     * @param file
     *            file that is written to. Is created if it doesn't exist
     * @return whether the writing was successful (true if no IOException
     *         occurred)
     */
    public static boolean append(final String txt, final File file) {
        boolean success = true;
        final String containingDir = file.getParent();
        // Make sure the directories leading to the file exist
        makeDir(containingDir);
        try {
            Files.append(txt, file, Charsets.UTF_8);
        } catch (final IOException e) {
            LOG.error(COULD_NOT_WRITE_TO_FILE, file, e);
            success = false;
        }
        return success;
    }

    /**
     * Converts a byte array to a {@code file}.
     *
     * @param byteArr
     *            byte array containing the data
     * @param file
     *            to be written to
     * @return true if and only if the given byte array could be written into
     *         the given {@code file}
     */
    public static boolean byteArrayToFile(final byte[] byteArr, final File file) {
        boolean success = false;
        if (file != null && byteArr != null) {
            try (FileOutputStream fos = new FileOutputStream(file)) {
                fos.write(byteArr);
                success = true;
            } catch (final IOException e) {
                LOG.warn(e.getMessage(), e);
            }
        }
        return success;
    }

    /**
     * Deletes a directory and all files and folders in it. If the argument is a
     * file, the file is deleted.
     *
     * @param dirOrFile
     *            directory or the file to delete
     */
    public static void deleteRecursively(final File dirOrFile) {
        if (dirOrFile.isDirectory()) {
            for (final File file : dirOrFile.listFiles()) {
                deleteRecursively(file);
            }
        }
        if (isEmptyDir(dirOrFile) || dirOrFile.isFile()) {
            final boolean success = dirOrFile.delete();
            if (!success) {
                LOG.warn("{} was not deleted successfully", dirOrFile);
            }
        }
    }

    /**
     * Converts a {@code file} to a byte array.
     * <p>
     * This will fail if the {@code file} is larger than two gigabytes.
     *
     * @param file
     *            whose data is converted to a byte array
     * @return a byte array representing the data in the file; null if an error
     *         occurs
     */
    public static byte[] fileToByteArray(final File file) {
        byte[] ba = null;
        if (file != null) {
            try (final FileInputStream is = new FileInputStream(file);) {
                ba = new byte[(int) file.length()];
                is.read(ba);
            } catch (final IOException e) {
                LOG.warn(e.getMessage(), e);
            }
        }
        return ba;
    }

    /**
     * Gets the numbers of characters in a {@code file}.
     *
     * @param file
     *            {@link File} whose characters are counted
     * @return the number of characters in this {@code file}
     */
    public static int getCharCount(final File file) {
        final String content = read(file);
        return content.length();
    }

    /**
     * @return the current directory as a {@code Path}
     */
    public static Path getCurrDir() {

        final String currDir = StandardSystemProperty.USER_DIR.value();
        return FileSystems.getDefault().getPath(currDir, "");

    }

    /**
     * Gets the name of a file from its path.
     * <p>
     * Also works with paths that don't exist on the file system.
     *
     * @param filePath
     *            path to a file or directory
     * @return the filename from the path
     */
    public static String getFileName(final String filePath) {
        final File f = new File(filePath);
        return f.getName();
    }

    /**
     * Gets the file name from a {@link URL}.
     *
     * @param url
     *            {@link URL} pointing to the file
     * @return the filename from the {@code url}
     */
    public static String getFileName(final URL url) {
        final String file = url.getFile();
        return getFileName(file);
    }

    /**
     * Gets the containing directory of a file or directory.
     * <p>
     * The directory is described as its absolute path in string from.
     *
     * @param fileOrDir
     *            the file or directory whose containing directory we we want to
     *            know
     * @return the absolute path of the parent directory of the
     *         {@code fileOrDir} as a string or an absent {@link Optional} if
     *         the file doesn't exist or is null
     */
    public static Optional<String> getParent(final File fileOrDir) {

        String parentDirPath = null;
        if (fileOrDir != null && fileOrDir.exists()) {
            final File parent = fileOrDir.getAbsoluteFile().getParentFile();
            if (parent == null) {
                LOG.info("{} exists but has no parent directory.", fileOrDir);
            } else {
                parentDirPath = parent.getAbsolutePath();
            }
        }
        return Optional.fromNullable(parentDirPath);
    }

    /**
     * Loads a resource and returns it as a {@code File}.
     *
     * @param path
     *            path to the resource
     * @param caller
     *            class of the caller
     * @return the resource as a {@code File}
     * @see Class#getResource(String)
     */
    public static File getResource(final String path, final Class<?> caller) {
        final URL resourceUrl = caller.getResource(path);
        File resource;
        if (resourceUrl == null) {
            LOG.warn("Did not find resource at {}", path);
            resource = new File("");
        } else {
            resource = new File(resourceUrl.getFile());
        }
        return resource;
    }

    /**
     * Gets a variable value from a file.
     *
     * @param key
     *            name of the variable containing the value
     * @param file
     *            file containing the variable
     * @param varPattern
     *            regex pattern to identify lines with variables. Must remember
     *            the key and the value as one group each
     * @return the value wrapped in an {@link Optional} in case the value wasn't
     *         found
     * @throws KeyNotFoundException
     *             thrown when the {@code key} is not found in the {@code file}
     */
    public static String getVal(final String key, final File file,
            final Pattern varPattern) throws KeyNotFoundException {

        String value = null;
        try {
            final List<String> lines = Files.readLines(file, Charsets.UTF_8);
            for (final String line : lines) {
                final Matcher m = varPattern.matcher(line);
                if (m.matches()) {
                    // The zeroth item in the group is the whole match
                    final String varName = m.group(1);
                    final String varVal = m.group(2);
                    if (varName.equals(key)) {
                        value = varVal;
                        break;
                    }
                }

            }
        } catch (final IOException e) {
            LOG.error(COULD_NOT_READ_FILE, file, e);

        }
        if (value == null) {
            throw new KeyNotFoundException(key, file);
        } else {
            return value;
        }

    }

    /**
     * Checks if the file is an empty directory, meaning it doesn't contain any
     * files.
     * <p>
     * Return false if the {@code dir} is not a directory but a file.
     *
     * @param dir
     *            directory that may be empty
     * @return whether the directory is empty, or false, if a file was passed
     */
    public static boolean isEmptyDir(final File dir) {
        boolean isEmpty = false;
        if (dir.isDirectory()) {
            isEmpty = dir.listFiles().length == 0;
        }
        return isEmpty;
    }

    /**
     * Joins two paths and creates a {@link File} from the combined path.
     * <p>
     * This does not create a file or directory on the file system. The paths
     * are separated by {@value #DIR_SEP}.
     *
     * @param path1
     *            first part of the combined path
     * @param path2
     *            second part of the combined path
     * @return {@code path1} and {@code path2} combined to a {@link File}
     */
    public static File join(final String path1, final String path2) {
        final String combinedPath = path1 + DIR_SEP + path2;

        return new File(combinedPath);
    }

    /**
     * Gets all file paths within a directory.
     *
     * @param startDir
     *            directory to start the listing of file paths
     * @param recursive
     *            whether to descend into subfolders of the {@code startDir}
     * @return the file paths in this directory
     */
    public static List<Path> listFiles(final Path startDir,
            final boolean recursive) {
        return listFiles(startDir, recursive, MATCH_ALL);
    }

    /**
     *
     * Gets all file paths within a directory.
     *
     * @param startDir
     *            directory to start the listing of file paths
     * @param recursive
     *            whether to descend into subfolders of the {@code startDir}
     * @param filterRegex
     *            only return file paths that match this regular expression
     *
     * @return the file paths contained in {@code startDir}
     */
    public static List<Path> listFiles(final Path startDir,
            final boolean recursive, final String filterRegex) {
        final List<Path> paths = new ArrayList<>();
        listFiles(paths, startDir, recursive, filterRegex);
        return paths;
    }

    /**
     * Creates an empty directory. All parent directories are created if they
     * don't exist.
     *
     * @param dirPath
     *            directory path. If it is null, this method returns
     *            {@code false}.
     * @return whether a new directory was created (false if there already
     *         existed one with the same name)
     */
    public static boolean makeDir(final String dirPath) {
        boolean newDirWasCreated;
        if (dirPath == null) {
            newDirWasCreated = false;
        } else {
            // Return whether the directory is new
            newDirWasCreated = new File(dirPath).mkdirs();
        }
        return newDirWasCreated;
    }

    /**
     * Makes a deep copy from a serializable object.
     *
     * @param copyObject
     *            of which a deep copy is made
     * @return the deep copy of the object
     */
    public static Serializable objectDeepCopy(final Serializable copyObject) {
        final OutputStream outputStream = new ByteArrayOutputStream();
        SerializationUtils.serialize(copyObject, outputStream);
        return (Serializable) outputStream;
    }

    /**
     * Extracts a map of variable names and corresponding values from a file.
     *
     * @param file
     *            file to parse containing the variables
     * @param pattern
     *            regular expression that describes a line with a variable name
     *            and its value. It is expected that the regex remembers the
     *            variable name as the first group and the variable value as the
     *            second group
     * @return a mapping from the variable name to its value
     */
    public static Map<String, String> parseVarsFromFile(final File file,
            final Pattern pattern) {
        final Map<String, String> varNamesAndValues = new HashMap<>();
        try {
            final List<String> lines = Files.readLines(file, Charsets.UTF_8);

            for (final String line : lines) {
                final Matcher m = pattern.matcher(line);
                if (m.matches()) {
                    // The zeroth item in the group is the whole match
                    final String varName = m.group(1);
                    final String varVal = m.group(2);
                    varNamesAndValues.put(varName, varVal);
                }
            }
        } catch (final IOException e) {
            LOG.warn(COULD_NOT_PARSE_FILE, file, e);
        }
        return varNamesAndValues;
    }

    /**
     * Reads a file line by line and returns its contents as a string.
     * <p>
     * Return the empty string if an {@link IOException} occurred. The character
     * set used is {@code UTF-8}.
     * <p>
     * The lines are separated using the system-dependent {@link #EOL}
     * character.
     *
     * @param file
     *            {@link File} to read
     * @return the contents of the file or the empty string if the file could
     *         not be read
     */
    public static String read(final File file) {
        String contents = "";
        if (file != null) {
            try {
                final List<String> lines = Files
                        .readLines(file, Charsets.UTF_8);
                contents = Joiner.on(EOL).join(lines);
            } catch (final IOException e) {
                LOG.warn(COULD_NOT_READ_FILE, file.getAbsolutePath(), e);
            }
        }
        return contents;
    }

    /**
     * Reads a file line by line and returns them as a list of strings.
     * <p>
     * Return an empty list if an {@link IOException} occurred. The character
     * set used is {@code UTF-8}.
     *
     * @param file
     *            {@link File} to read
     * @return the contents of the file as a list of strings or an empty list if
     *         the file could not be read
     */
    public static List<String> readLines(final File file) {
        final List<String> lines = new ArrayList<>();
        try {
            lines.addAll(Files.readLines(file, Charsets.UTF_8));
        } catch (final IOException e) {
            LOG.warn(COULD_NOT_READ_FILE, file.getAbsolutePath(), e);
        }
        return lines;
    }

    /**
     * Sets a value of a property within a file.
     *
     * @param property
     *            name of the property that gets the new value
     * @param newVal
     *            new value the property has after the update
     * @param file
     *            absolute path to the file containing the property
     * @param propValPattern
     *            property/value {@link Pattern} that determines how a property
     *            and its value look like in the file. It's expected that the
     *            regex remembers the variable name as its first group and the
     *            variable value as its second
     * @return whether the update was successful (the property must exist in the
     *         file)
     */
    public static boolean setVal(final String property, final String newVal,
            final File file, final Pattern propValPattern) {
        // The lines of the updated file
        final List<String> newLines = new ArrayList<>();
        boolean propWasFound = false;
        try {
            final List<String> lines = Files.readLines(file, Charsets.UTF_8);
            for (String line : lines) {
                final Matcher m = propValPattern.matcher(line);
                if (m.matches()) {
                    // The zeroth item in the group is the whole match
                    final String name = m.group(1);
                    final String oldVal = m.group(2);
                    if (property.equals(name)) {
                        // Update the value of the variable
                        final String strVal = String.valueOf(newVal);
                        line = line.replaceFirst(oldVal, strVal);
                        propWasFound = true;
                    }
                }
                newLines.add(line);
            }
        } catch (final IOException e) {
            LOG.error(COULD_NOT_READ_FILE, file, e);
        }
        // Write the -maybe changed- contents to file
        final String newFileContent = Joiner.on(EOL).join(newLines);
        write(newFileContent, file);
        return propWasFound;
    }

    /**
     * Converts a string representing a file or directory to a {@link Path}.
     *
     * @param pathStr
     *            file path as a string
     * @return file or directory as a {@link Path}
     * @throws InvalidPathException
     *             thrown on Windows when {@code pathStr} does not represent a
     *             valid path (e.g., because it contains invalid characters like
     *             ?, |, or *)
     */
    public static Path toPath(final String pathStr) {

        final File pathAsFile = new File(pathStr);
        final URI pathUri = pathAsFile.toURI();
        return Paths.get(pathUri);
    }

    /**
     * Writes a string to a file. Overwrites previous content. Creates the file
     * and the needed directories if they do not exist.
     * <p>
     * UTF-8 is used for encoding the file's content.
     *
     * @param txt
     *            text that is written to the file
     * @param file
     *            file that is written to. Is created if it doesn't exist
     * @return whether the writing was successful (true if no IOException
     *         occurred)
     */
    public static boolean write(final String txt, final File file) {
        boolean success = true;
        final String containingDir = file.getParent();
        // Make sure the directories leading to the file exist
        makeDir(containingDir);
        try {
            Files.write(txt, file, Charsets.UTF_8);
        } catch (final IOException e) {
            LOG.error(COULD_NOT_WRITE_TO_FILE, file, e);
            success = false;
        }
        return success;
    }

    /**
     * Gets all file paths in a directory. Calls itself to descend into
     * directories.
     *
     * @param paths
     *            file paths of the directory are added to this list
     * @param currDir
     *            current directory we are in
     * @param recursive
     *            whether to visit other directories recursively
     * @param regexFilter
     *            only add a file if it matches this regular expression
     */
    private static void listFiles(final List<Path> paths, final Path currDir,
            final boolean recursive, final String regexFilter) {
        if (currDir.toFile().isDirectory()) {
            try (DirectoryStream<Path> stream = java.nio.file.Files
                    .newDirectoryStream(currDir)) {
                for (final Path path : stream) {
                    final String fileName = path.toString();
                    if (fileName.matches(regexFilter)) {
                        paths.add(path);
                    }
                    if (recursive) {
                        listFiles(paths, path, recursive, regexFilter);
                    }
                }
            } catch (final IOException e) {
                LOG.error(COULD_NOT_OPEN_DIR, currDir, e);
            }
        }
    }
}
TOP

Related Classes of eu.bges.IOUtil

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.