IoUtils.java

/*
 * @copyright defined in LICENSE.txt
 */

package hera.util;

import static hera.util.ValidationUtils.assertNotNull;
import static org.slf4j.LoggerFactory.getLogger;

import com.google.common.io.BaseEncoding;
import java.io.ByteArrayOutputStream;
import java.io.Flushable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.slf4j.Logger;

public class IoUtils {
  protected static final Logger logger = getLogger(IoUtils.class);

  /**
   * Call #flush if possible.
   * <p>
   * Call #flush if the element of {@code flushables} is next:
   * </p>
   * <ul>
   * <li>{@link Flushable}</li>
   * </ul>
   *
   * @param flushable object instances to flush
   */
  public static void tryFlush(final Object... flushable) {
    for (final Object obj : flushable) {
      try {
        if (obj instanceof Flushable) {
          ((Flushable) obj).flush();
        }
      } catch (final Throwable th) {
        logger.trace("Ignore exception: {}", th);
      }
    }
  }

  /**
   * Process streaming.
   *
   * @param in {@link InputStream}
   * @param consumer instance to use streaming
   *
   * @return read bytes
   *
   * @throws IOException If fail to read or process
   */
  public static int stream(final InputStream in, final StreamConsumer consumer) throws Exception {
    assertNotNull(in);

    final byte[] buffer = new byte[1024];
    int readBytes;
    int sum = 0;
    while (0 < (readBytes = in.read(buffer))) {
      consumer.apply(buffer, 0, readBytes);
      sum += readBytes;
    }

    return sum;
  }

  /**
   * Redirect {@code from} to {@code to}.
   *
   * @param from {@link InputStream} to read
   * @param to {@link OutputStream} to write
   *
   * @return the number of bytes to redirect
   *
   * @throws IOException Fail to read or write
   */
  public static int redirect(final InputStream from, final OutputStream to) throws IOException {
    assertNotNull(to);
    try {
      return stream(from, new StreamConsumer() {
        @Override
        public void apply(byte[] bytes, int offset, int length) throws Exception {
          to.write(bytes, offset, length);
        }
      });
    } catch (final Exception e) {
      throw (IOException) e;
    }
  }

  /**
   * Redirect {@code from} to {@code to}.
   *
   * @param from {@link Reader} to read
   * @param to {@link Writer} to write
   *
   * @return the number of bytes to redirect
   *
   * @throws IOException Fail to read or write
   */
  public static int redirect(final Reader from, final Writer to) throws IOException {
    assertNotNull(from);
    assertNotNull(to);

    final char[] buffer = new char[1024];
    int readBytes;
    int sum = 0;
    while (0 < (readBytes = from.read(buffer))) {
      to.write(buffer, 0, readBytes);
      sum += readBytes;
    }

    return sum;
  }

  /**
   * Read from {@code in} and return all bytes.
   *
   * @param in {@link InputStream} to read
   *
   * @return read bytes
   *
   * @throws IOException Fail to read
   */
  public static byte[] from(
      final InputStream in)
      throws IOException {
    final ByteArrayOutputStream byteOut = new ByteArrayOutputStream();

    final byte[] bytes = new byte[10000];
    int readBytes;
    while (0 < (readBytes = in.read(bytes))) {
      byteOut.write(bytes, 0, readBytes);
    }
    return byteOut.toByteArray();
  }

  /**
   * Read from {@code reader} and return string.
   *
   * @param reader {@link Reader} to read
   * @return read string
   *
   * @throws IOException Fail to read
   */
  public static String from(
      final Reader reader)
      throws IOException {
    final StringWriter writer = new StringWriter();

    final char[] bytes = new char[10000];
    int readBytes;
    while (0 < (readBytes = reader.read(bytes))) {
      writer.write(bytes, 0, readBytes);
    }
    return writer.toString();
  }

  /**
   * Calculate checksum.
   *
   * @param in {@link InputStream} containing content
   * @return checksum
   * @throws IOException Fail to read content
   * @throws NoSuchAlgorithmException No MD5 algorithm
   */
  public static byte[] getChecksum(final InputStream in)
      throws IOException, NoSuchAlgorithmException {
    final MessageDigest checksumGenerator = MessageDigest.getInstance("MD5");

    final DigestInputStream dis = new DigestInputStream(in, checksumGenerator);
    int readBytes = redirect(dis, new ByteArrayOutputStream());
    logger.debug("{} byte(s) read", readBytes);
    return checksumGenerator.digest();
  }

  /**
   * Calculate checksum as string.
   *
   * @param in {@link InputStream} containing content
   * @return checksum
   * @throws IOException Fail to read content
   * @throws NoSuchAlgorithmException No MD5 algorithm
   */
  public static String getChecksumAsString(final InputStream in)
      throws IOException, NoSuchAlgorithmException {
    final byte[] bytes = getChecksum(in);
    return BaseEncoding.base64().encode(bytes);
  }
}