org.ut.biolab.medsavant.shared.util.IOUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.ut.biolab.medsavant.shared.util.IOUtils.java

Source

/**
 * See the NOTICE file distributed with this work for additional information
 * regarding copyright ownership.
 *
 * This 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 2.1 of the License, or (at your option)
 * any later version.
 *
 * This software 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 software; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF
 * site: http://www.fsf.org.
 */
package org.ut.biolab.medsavant.shared.util;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.GZIPInputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
import org.apache.commons.io.FileExistsException;
import org.apache.commons.io.FileUtils;

/**
 * I/O-related utility methods. Functions for manipulating Savant files are in
 * SavantFileUtils.
 *
 * @author mfiume, tarkvara
 */
public class IOUtils {

    private static final Log LOG = LogFactory.getLog(IOUtils.class);

    /**
     * 
     * @param inputFile - the file to check
     * @param directory - the directory to test for.
     * @return true if 'directory' is an ancestor of 'inputFile', false otherwise.
     * @throws IOException 
     */
    public static boolean isInDirectory(File inputFile, File directory) throws IOException {
        if (inputFile.isDirectory() && inputFile.getAbsolutePath().equals(directory.getAbsolutePath())) {
            return true;
        }

        File parent = inputFile.getParentFile();
        while (parent != null) {
            if (parent.getCanonicalPath().equals(directory.getCanonicalPath())) {
                return true;
            }
            parent = parent.getParentFile();
        }

        return false;
    }

    /**
     * Deletes the directory p, recursing upwards through the directory tree and deleting all empty
     * parent directories until it reaches the ancestor directory 'stopAtDir'. Immediately returns
     * if 'stopAtDir' is not an ancestor of p.  
     * 
     * @param p - The nested directory to delete.
     * @param stopAtDir - the ancestor directory to stop at (this directory will NOT be deleted, even if empty).
     * @throws IOException 
     */
    public static void deleteEmptyParents(File p, File stopAtDir) throws IOException {
        if (!isInDirectory(p, stopAtDir)) {
            return;
        }
        if (p != null && p.isDirectory() && !p.getAbsolutePath().equals(stopAtDir.getAbsolutePath())) {
            if (p.listFiles().length == 0) {
                File parent = p.getParentFile();
                p.delete();
                deleteEmptyParents(parent, stopAtDir);
            }
        }
    }

    public static void copyFile(File srcFile, File destFile) throws IOException {
        if (srcFile.equals(destFile)) {
            return;
        }
        copyStream(new FileInputStream(srcFile), new FileOutputStream(destFile));
    }

    public static void copyDir(File srcDir, File destDir) throws IOException {
        File[] files = srcDir.listFiles();
        if (files != null) {
            for (File f : files) {
                copyFile(f, new File(destDir, f.getName()));
            }
        }
    }

    public static void copyStream(InputStream input, OutputStream output) throws IOException {
        byte[] buf = new byte[8192];
        int len;
        long tot = 0;
        try {
            while ((len = input.read(buf)) > 0) {
                tot += len;
                output.write(buf, 0, len);
            }
            LOG.info("Copied/extracted " + tot + " bytes");
        } catch (IOException x) {
            // There's a bug in BlockCompressedInputStream where it throws an IOException instead of doing a proper EOF.
            // Suppress this exception, but throw real ones.
            if (!x.getMessage().equals("Unexpected compressed block length: 1")) {
                x.printStackTrace();
                throw x;
            }
        } finally {
            input.close();
            output.close();
        }
    }

    /**
     * Cheesy method which lets us read from an InputStream without having to
     * instantiate a BufferedReader. Intended to get around some glitches
     * reading GZIPInputStreams over an HTTP stream.
     */
    public static String readLine(InputStream input) throws IOException {
        StringBuilder buf = new StringBuilder();
        int c;
        while ((c = input.read()) >= 0 && c != '\n') {
            buf.append((char) c);
        }
        return c >= 0 ? buf.toString() : null;
    }

    /**
     * Recursively delete a directory.
     */
    public static boolean deleteDirectory(File path) {
        if (path.exists()) {
            File[] files = path.listFiles();
            for (int i = 0; i < files.length; i++) {
                if (files[i].isDirectory()) {
                    deleteDirectory(files[i]);
                } else {
                    files[i].delete();
                }
            }
        }
        return path.delete();
    }

    /**
     * Make sure this directory and all its parents have world execute
     * permissions. This is necessary to ensure that MySQL can write files to
     * the given directory.
     *
     * @param base
     * @return
     */
    public static void checkForWorldExecute(File f) throws IOException {
        //File f = base.isDirectory() ? base : base.getParentFile();
        if (hasWorldExecute(f)) {
            /*f = f.getParentFile();
             if (f == null) {
             // Reached /
             return;
             }*/
            return;
        }
        throw new IOException(f + " did not have execute permissions.");
    }

    /**
     * Retrieve the permission string (e.g. "rwxrwxrwx") for the given file.
     * TODO: implement this in a less Unix-specific manner.
     *
     * @param f the file or directory whose permissions we are checking
     */
    private static String getPermissionString(File f) throws IOException {
        Process proc = Runtime.getRuntime().exec("ls -ld " + f);
        BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream()));
        String line = null;
        String result = null;
        while ((line = reader.readLine()) != null) {
            int spacePos = line.indexOf(' ');
            if (spacePos > 0) {
                result = line.substring(1, spacePos);
            }
        }
        //LOG.info("Permission of " + f.getAbsolutePath() + " is " + result);
        return result;
    }

    /**
     * Does the given directory have world-execute permissions? TODO: implement
     * this in a less Unix-specific manner.
     *
     * @param dir the directory to be checked
     */
    private static boolean hasWorldExecute(File f) throws IOException {
        String perm = getPermissionString(f);
        if (perm != null && perm.length() >= 9) {
            return perm.charAt(8) == 'x';
        }
        return false;
    }

    private static List<File> unArchive(File dest, ArchiveInputStream ais) throws IOException {
        List<File> files = new ArrayList<File>();
        ArchiveEntry archiveEntry = ais.getNextEntry();
        // tarIn is a TarArchiveInputStream
        while (archiveEntry != null) {// create a file with the same name as the tarEntry            
            File destPath = new File(dest, archiveEntry.getName());

            if (archiveEntry.isDirectory()) {
                destPath.mkdirs();
            } else {
                destPath.createNewFile();
                byte[] btoRead = new byte[1024];

                BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(destPath));

                int len = 0;
                while ((len = ais.read(btoRead)) != -1) {
                    bout.write(btoRead, 0, len);
                }

                bout.close();
                btoRead = null;
                files.add(destPath);
            }

            archiveEntry = ais.getNextEntry();
        }
        ais.close();
        return files;
    }

    /**
     * Strips the last extension from the filename corresponding to f, 
     * and returns a new File for this new filename, or null if 
     * the file was not found to have an extension.    
     */
    public static String stripExtension(File f) {
        String filename = f.getName();
        for (int i = filename.length() - 1; i > 0; --i) {
            if (filename.charAt(i) == '.') {
                return filename.substring(0, i);
            }
        }
        return null;
    }

    /**
     * Moves a file from one location to another using apache io library.
     * Use this instead of File.renameTo
     * 
     * @param src - the path to the source file
     * @param dst - the path to the destination file
     * @return 
     */
    public static boolean moveFile(File src, File dst) {
        try {
            FileUtils.moveFile(src, dst);
        } catch (FileExistsException fee) {
            LOG.error("Error while moving file", fee);
            return false;
        } catch (IOException ie) {
            LOG.error("Error while moving File", ie);
            return false;
        }
        return true;
    }

    /**
     * @return true if the File is a compressed file, archive, or compressed archive.
     */
    public static boolean isArchive(File f) {
        String n = f.getName().toLowerCase();
        return n.endsWith(".zip") || n.endsWith(".bz2") || n.endsWith(".tgz") || n.endsWith(".gz")
                || n.endsWith(".tar");
    }

    /**
     * Decompresses/unarchives the file given by 'f' to the destination path
     * given by dest, and returns a list of all the files contained within the archive.
     * If the input file is compressed, but not archived, the destination filename will be 
     * "dest/"+stripExtension(f.getName()).  If the input file is 
     * not compressed or archived, the input file will be moved to the new location 
     * given by dest, and returned in a one-element list.
     * 
     * Note the file given by f will be deleted.
     *
     * @return A list of files that were decompressed from the input file f, or
     * a list containing the input file in its new location if the input file was not compressed/archived.
     * @throws IOException 
     * @see stripExtension
     */
    public static List<File> decompressAndDelete(File f, File dest) throws IOException {
        String lcfn = f.getName().toLowerCase();
        InputStream is = new BufferedInputStream(new FileInputStream(f));
        List<File> files;

        //Detect compression type.
        if (lcfn.endsWith(".gz") || lcfn.endsWith(".tgz")) {
            is = new GzipCompressorInputStream(is, true);
        } else if (lcfn.endsWith(".bz2")) {
            is = new BZip2CompressorInputStream(is, true);
        }

        //Detect archive type.
        if (lcfn.endsWith(".tgz") || lcfn.endsWith(".tar.gz") || lcfn.endsWith(".tar")
                || lcfn.endsWith(".tar.bz2")) {
            is = new TarArchiveInputStream(is);
            files = unArchive(dest, (ArchiveInputStream) is);
        } else if (lcfn.endsWith(".zip")) {
            is = new ZipArchiveInputStream(is);
            files = unArchive(dest, (ArchiveInputStream) is);
        } else {
            String filename = f.getName();
            if (!(is instanceof BufferedInputStream)) {
                filename = stripExtension(f);
                if (filename == null) {
                    filename = f.getName() + ".decompressed";
                }
            }

            File outputFile = new File(dest, filename);
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(outputFile));
            if (!(is instanceof BufferedInputStream)) {
                IOUtils.copyStream(is, bos);
            } else {
                if (!moveFile(f, outputFile)) {
                    throw new IOException(
                            "Couldn't move file " + f.getAbsolutePath() + " to " + outputFile.getAbsolutePath());
                }
            }

            bos.close();
            files = new ArrayList<File>(1);
            files.add(outputFile);
        }
        is.close();
        if (f.exists()) {
            f.delete();
        }
        return files;
    }

    /**
     * Checks if an input stream is gzipped.
     *
     * @param in
     * @return
     */
    public static boolean isGZipped(InputStream in) {
        if (!in.markSupported()) {
            in = new BufferedInputStream(in);
        }
        in.mark(2);
        int magic = 0;
        try {
            magic = in.read() & 0xff | ((in.read() << 8) & 0xff00);
            in.reset();
        } catch (IOException e) {
            e.printStackTrace(System.err);
            return false;
        }
        return magic == GZIPInputStream.GZIP_MAGIC;
    }

    /**
     * Checks if a file is gzipped.
     *
     * @param f
     * @return
     */
    public static boolean isGZipped(File f) {
        int magic = 0;
        try {
            RandomAccessFile raf = new RandomAccessFile(f, "r");
            magic = raf.read() & 0xff | ((raf.read() << 8) & 0xff00);
            raf.close();
        } catch (Throwable e) {
            e.printStackTrace(System.err);
        }
        return magic == GZIPInputStream.GZIP_MAGIC;
    }

    /**
     * Checks if a file is zipped.
     *
     * @param f
     * @return
     */
    public static boolean isZipped(File f) {

        try {
            RandomAccessFile raf = new RandomAccessFile(f, "r");
            long n = raf.readInt();
            raf.close();
            if (n == 0x504B0304) {
                return true;
            } else {
                return false;
            }
        } catch (Throwable e) {
            e.printStackTrace(System.err);
            return false;
        }

    }

    /**
     * Unzip a zip file
     *
     * @param zipFile Path to the zip file
     * @param toPath Destination path
     * @throws ZipException
     * @throws IOException
     */
    public static List<File> unzipFile(File file, String toPath) throws ZipException, IOException {
        int BUFFER = 2048;

        ZipFile zip = new ZipFile(file);

        //new File(newPath).mkdir();
        Enumeration zipFileEntries = zip.entries();

        List<File> files = new ArrayList<File>();

        // Process each entry
        while (zipFileEntries.hasMoreElements()) {
            // grab a zip file entry
            ZipEntry entry = (ZipEntry) zipFileEntries.nextElement();
            String currentEntry = entry.getName();
            File destFile = new File(toPath, currentEntry);
            File destinationParent = destFile.getParentFile();

            // create the parent directory structure if needed
            destinationParent.mkdirs();

            if (!entry.isDirectory()) {
                BufferedInputStream is = new BufferedInputStream(zip.getInputStream(entry));
                int currentByte;
                // establish buffer for writing file
                byte data[] = new byte[BUFFER];

                // write the current file to disk
                FileOutputStream fos = new FileOutputStream(destFile);
                BufferedOutputStream dest = new BufferedOutputStream(fos, BUFFER);

                // read and write until last byte is encountered
                while ((currentByte = is.read(data, 0, BUFFER)) != -1) {
                    dest.write(data, 0, currentByte);
                }
                dest.flush();
                dest.close();
                is.close();
            }

            if (currentEntry.toLowerCase().endsWith(".zip") && isZipped(destFile)) {
                // found a zip file, try to open
                files.addAll(unzipFile(destFile, new File(destFile.getAbsolutePath()).getParent()));
            } else {
                files.add(destFile);
            }
        }

        return files;
    }

    public static File zipDirectory(File dir, File outFile) throws IOException {
        ZipOutputStream out = new ZipOutputStream(new FileOutputStream(outFile));
        zipRecursively(".", dir, out);
        out.close();
        return outFile;
    }

    private static void zipRecursively(String path, File dir, ZipOutputStream out) throws IOException {
        for (File f : dir.listFiles()) {
            writeFileToZipStream(path, f, out);
            if (f.isDirectory()) {
                zipRecursively(path + "/" + f.getName() + "/", f, out);
            }
        }
    }

    private static void writeFileToZipStream(String path, File f, ZipOutputStream out) throws IOException {
        // name the file inside the zip  file

        if (f.isFile()) {
            out.putNextEntry(new ZipEntry(path + "/" + f.getName()));

            FileInputStream in = new FileInputStream(f);
            byte[] b = new byte[1024];
            int count;
            while ((count = in.read(b)) > 0) {
                out.write(b, 0, count);
            }
            in.close();
        } else if (f.isDirectory()) {
            out.putNextEntry(new ZipEntry(f.getName() + "/"));
        }
    }

    public static File zipFile(File file, File outputFile) throws FileNotFoundException, IOException {

        ZipOutputStream out = new ZipOutputStream(new FileOutputStream(outputFile));
        writeFileToZipStream(".", file, out);
        out.close();

        return outputFile;
    }
}