net.myrrix.common.io.IOUtils.java Source code

Java tutorial

Introduction

Here is the source code for net.myrrix.common.io.IOUtils.java

Source

/*
 * Copyright Myrrix Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.myrrix.common.io;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import java.util.zip.InflaterInputStream;
import java.util.zip.ZipInputStream;

import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
import com.google.common.io.ByteStreams;
import com.google.common.io.CharStreams;
import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;

import net.myrrix.common.ClassUtils;

/**
 * Simple utility methods related to I/O.
 *
 * @author Sean Owen
 * @since 1.0
 */
public final class IOUtils {

    private IOUtils() {
    }

    /**
     * @param raw string to URL-encode
     * @return the URL encoding of the argument, using the UTF-8 encoding if necessary to interpret
     *  characters as bytes
     */
    public static String urlEncode(String raw) {
        try {
            return URLEncoder.encode(raw, Charsets.UTF_8.name());
        } catch (UnsupportedEncodingException uee) {
            // Can't happen for UTF-8
            throw new AssertionError(uee);
        }
    }

    /**
     * Attempts to recursively delete a directory. This may not work across symlinks.
     *
     * @param dir directory to delete along with contents
     * @return {@code true} if all files and dirs were deleted successfully
     */
    public static boolean deleteRecursively(File dir) {
        if (dir == null) {
            return false;
        }
        Deque<File> stack = new ArrayDeque<File>();
        stack.push(dir);
        boolean result = true;
        while (!stack.isEmpty()) {
            File topElement = stack.peek();
            if (topElement.isDirectory()) {
                File[] directoryContents = topElement.listFiles();
                if (directoryContents != null && directoryContents.length > 0) {
                    for (File fileOrSubDirectory : directoryContents) {
                        stack.push(fileOrSubDirectory);
                    }
                } else {
                    result = result && stack.pop().delete();
                }
            } else {
                result = result && stack.pop().delete();
            }
        }
        return result;
    }

    /**
     * Opens an {@link InputStream} to the file. If it appears to be compressed, because its file name ends in
     * ".gz" or ".zip" or ".deflate", then it will be decompressed accordingly
     *
     * @param file file, possibly compressed, to open
     * @return {@link InputStream} on uncompressed contents
     * @throws IOException if the stream can't be opened or is invalid or can't be read
     */
    public static InputStream openMaybeDecompressing(File file) throws IOException {
        String name = file.getName();
        InputStream in = new FileInputStream(file);
        if (name.endsWith(".gz")) {
            return new GZIPInputStream(in);
        }
        if (name.endsWith(".zip")) {
            return new ZipInputStream(in);
        }
        if (name.endsWith(".deflate")) {
            return new InflaterInputStream(in);
        }
        if (name.endsWith(".bz2") || name.endsWith(".bzip2")) {
            return new BZip2CompressorInputStream(in);
        }
        return in;
    }

    /**
     * @param file file, possibly compressed, to open
     * @return {@link Reader} on uncompressed contents
     * @throws IOException if the stream can't be opened or is invalid or can't be read
     * @see #openMaybeDecompressing(File) 
     */
    public static Reader openReaderMaybeDecompressing(File file) throws IOException {
        return new InputStreamReader(openMaybeDecompressing(file), Charsets.UTF_8);
    }

    /**
     * @param in   stream to read and copy
     * @param file file to write stream's contents to
     * @throws IOException if the stream can't be read or the file can't be written
     */
    public static void copyStreamToFile(InputStream in, File file) throws IOException {
        FileOutputStream out = new FileOutputStream(file);
        try {
            ByteStreams.copy(in, out);
        } finally {
            out.close();
        }
    }

    /**
     * @param url  URL whose contents are to be read and copied
     * @param file file to write contents to
     * @throws IOException if the URL can't be read or the file can't be written
     */
    public static void copyURLToFile(URL url, File file) throws IOException {
        InputStream in = url.openStream();
        try {
            copyStreamToFile(in, file);
        } finally {
            in.close();
        }
    }

    /**
     * @param url URL whose contents are to be read
     * @return the contents of the URL, interpreted as a UTF-8 encoded string
     * @throws IOException if the URL can't be read or the file can't be written
     */
    public static String readSmallTextFromURL(URL url) throws IOException {
        Reader in = new InputStreamReader(url.openStream(), Charsets.UTF_8);
        try {
            return CharStreams.toString(in);
        } finally {
            in.close();
        }
    }

    /**
     * @param delegate {@link OutputStream} to wrap
     * @return a {@link GZIPOutputStream} wrapping the given {@link OutputStream}. It attempts to use the new 
     *  Java 7 version that actually responds to {@link OutputStream#flush()} as expected. If not available,
     *  uses the previous version ({@link GZIPOutputStream#GZIPOutputStream(OutputStream)})
     */
    public static GZIPOutputStream buildGZIPOutputStream(OutputStream delegate) throws IOException {
        // In Java 7, GZIPOutputStream's flush() behavior can be made more as expected. Use it if possible
        // but fall back if not to the usual version
        try {
            return ClassUtils.loadInstanceOf(GZIPOutputStream.class,
                    new Class<?>[] { OutputStream.class, boolean.class }, new Object[] { delegate, true });
        } catch (IllegalStateException ignored) {
            return new GZIPOutputStream(delegate);
        }
    }

    /**
     * @see #buildGZIPOutputStream(OutputStream) 
     */
    public static GZIPOutputStream buildGZIPOutputStream(File file) throws IOException {
        return buildGZIPOutputStream(new FileOutputStream(file));
    }

    /**
     * @param delegate {@link OutputStream} to wrap
     * @return the result of {@link #buildGZIPOutputStream(OutputStream)} as a {@link Writer} that encodes
     *  using UTF-8 encoding
     */
    public static Writer buildGZIPWriter(OutputStream delegate) throws IOException {
        return new OutputStreamWriter(buildGZIPOutputStream(delegate), Charsets.UTF_8);
    }

    /**
     * @see #buildGZIPWriter(OutputStream)
     */
    public static Writer buildGZIPWriter(File file) throws IOException {
        return buildGZIPWriter(new FileOutputStream(file, false));
    }

    /**
     * Wraps its argument in {@link BufferedReader} if not already one.
     */
    public static BufferedReader buffer(Reader maybeBuffered) {
        return maybeBuffered instanceof BufferedReader ? (BufferedReader) maybeBuffered
                : new BufferedReader(maybeBuffered);
    }

    /**
     * @return a {@link BufferedReader} on the stream, using UTF-8 encoding
     */
    public static BufferedReader bufferStream(InputStream in) {
        return new BufferedReader(new InputStreamReader(in, Charsets.UTF_8));
    }

    /**
     * @return true iff the given file is a gzip-compressed file with no content; the file itself may not
     *  be empty because it contains gzip headers and footers
     * @throws IOException if the file is not a gzip file or can't be read
     */
    public static boolean isGZIPFileEmpty(File f) throws IOException {
        InputStream in = new GZIPInputStream(new FileInputStream(f));
        try {
            return in.read() == -1;
        } finally {
            in.close();
        }
    }

    /**
     * @return object of type T that was serialized into the given file
     */
    public static <T extends Serializable> T readObjectFromFile(File f, Class<T> clazz) throws IOException {
        ObjectInputStream in = new ObjectInputStream(openMaybeDecompressing(f));
        try {
            @SuppressWarnings("unchecked")
            T result = (T) in.readObject();
            return result;
        } catch (ClassNotFoundException cnfe) {
            throw new IllegalStateException(cnfe);
        } finally {
            in.close();
        }
    }

    /**
     * Serializes an object, with gzip compression, to a given file.
     */
    public static <T extends Serializable> void writeObjectToFile(File f, T t) throws IOException {
        Preconditions.checkArgument(f.getName().endsWith(".gz"), "File should end in .gz: %s", f);
        ObjectOutputStream out = new ObjectOutputStream(buildGZIPOutputStream(f));
        try {
            out.writeObject(t);
        } finally {
            out.close();
        }
    }

}