Package propel.core.utils

Source Code of propel.core.utils.StreamUtils

// /////////////////////////////////////////////////////////
// This file is part of Propel.
//
// Propel 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.
//
// Propel 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 Propel. If not, see <http://www.gnu.org/licenses/>.
// /////////////////////////////////////////////////////////
// Authored by: Nikolaos Tountas -> salam.kaser-at-gmail.com
// /////////////////////////////////////////////////////////
package propel.core.utils;

import java.io.ByteArrayOutputStream;
import java.io.CharArrayWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import lombok.Validate;
import lombok.Validate.NotNull;
import lombok.val;
import propel.core.common.CONSTANT;

/**
* Provides utility functionality for Stream-related classes
*/
public final class StreamUtils
{
  /**
   * Message used in thrown exception when a stream end is encountered prematurely.
   */
  public static final String EOF_EXCEPTION_MESSAGE_BYTES = "The stream " + CONSTANT.EOF
      + "ed unexpectedly, while %d extra bytes were expected.";
  /**
   * Message used in thrown exception when a stream end is encountered prematurely.
   */
  public static final String EOF_EXCEPTION_MESSAGE_CHARACTERS = "The stream " + CONSTANT.EOF
      + "ed unexpectedly, while %d extra characters were expected.";

  /**
   * Copies data from one stream to another. Reads all data from source stream, then writes it to destination, then the destination stream
   * is flushed.
   *
   * @throws NullPointerException An argument is null.
   * @throws IllegalArgumentException An argument is out of range.
   * @throws IOException An I/O exception occurred.
   */
  @Validate
  public static void copy(@NotNull final InputStream from, @NotNull final OutputStream to, final long length)
      throws IOException
  {
    if (length < 0)
      throw new IllegalArgumentException("length=" + length);
    if (length == 0)
      return;

    writeFully(to, readFully(from, length));
  }

  /**
   * Blocks until all data is written to the given stream. The stream is flushed.
   *
   * @throws NullPointerException An argument is null.
   * @throws IOException An I/O exception occurred.
   */
  @Validate
  public static void writeFully(@NotNull final OutputStream stream, @NotNull final byte[] data)
      throws IOException
  {
    stream.write(data, 0, data.length);
    stream.flush();
  }

  /**
   * Blocks until all data is written to the given stream. The stream is flushed. Performs multiple writes if the size if bigger than the
   * maximum allowed chunk size.
   *
   * @throws NullPointerException An argument is null.
   * @throws IllegalArgumentException An argument is out of range.
   * @throws IOException An I/O exception occurred.
   */
  @Validate
  public static void writeFully(@NotNull final OutputStream stream, @NotNull final byte[] data, final int maxChunkSize)
      throws IOException
  {
    if (maxChunkSize <= 0)
      throw new IllegalArgumentException("maxChunkSize=" + maxChunkSize);

    val length = data.length;

    if (length < maxChunkSize)
      stream.write(data, 0, length);
    else
    {
      int remainder = length % maxChunkSize;
      int chunks = length / maxChunkSize;
      for (int i = 0; i < chunks; i++)
        stream.write(data, i * maxChunkSize, maxChunkSize);

      if (remainder > 0)
        stream.write(data, chunks * maxChunkSize, remainder);
    }

    stream.flush();
  }

  /**
   * Blocks until all bytes are read from the stream, returns the decoded data using the UTF8 encoding.
   *
   * @throws NullPointerException An argument is null.
   * @throws IOException An I/O exception occurred.
   */
  public static String readAllCharacters(final InputStream stream)
      throws IOException
  {
    return readAllCharacters(stream, CONSTANT.UTF8);
  }

  /**
   * Blocks until all bytes are read from the stream, returns the decoded data using the specified encoding.
   *
   * @throws NullPointerException An argument is null.
   * @throws IOException An I/O exception occurred.
   */
  @Validate
  public static String readAllCharacters(@NotNull final InputStream stream, @NotNull final Charset streamEncoding)
      throws IOException
  {
    val data = readFully(stream, stream.available());
    return new String(data, streamEncoding);
  }

  /**
   * Blocks until a specified amount of bytes is read from a stream. Puts the data read into the specified array.
   *
   * @throws NullPointerException An argument is null.
   * @throws IllegalArgumentException An argument is out of range.
   * @throws IOException An I/O exception occurred.
   */
  @Validate
  public static void readFully(@NotNull final InputStream stream, @NotNull final byte[] buffer, final long length)
      throws IOException
  {
    if (length < 0)
      throw new IllegalArgumentException("length=" + length);
    if (buffer.length < length)
      throw new IllegalArgumentException("bufferLen=" + buffer.length + " length=" + length);
    if (length > Integer.MAX_VALUE)
      throw new IllegalArgumentException("length=" + length);

    int totalRemaining = (int) length;
    int totalRead = 0;

    while (totalRemaining > 0)
    {
      // attempt to read
      int read = stream.read(buffer, totalRead, totalRemaining);

      if (read <= 0)
        throw new IOException(String.format(EOF_EXCEPTION_MESSAGE_BYTES, totalRemaining));

      // maintain counters
      totalRead += read;
      totalRemaining -= read;
    }
  }

  /**
   * Blocks until a specified amount of bytes is read from a stream. Returns the data read.
   *
   * @throws NullPointerException An argument is null.
   * @throws IllegalArgumentException An argument is out of range.
   * @throws IOException An I/O exception occurred.
   */
  @Validate
  public static byte[] readFully(@NotNull final InputStream stream, final long length)
      throws IOException
  {
    if (length < 0)
      throw new IllegalArgumentException("length=" + length);
    if (length > Integer.MAX_VALUE)
      throw new IllegalArgumentException("length=" + length);

    val buffer = new byte[(int) length];
    readFully(stream, buffer, length);
    return buffer;
  }

  /**
   * Blocks until a specified amount of bytes is read from a stream. Puts the data read into the specified array. Performs 'chunked'
   * transfer if size if bigger than the maximum allowed packet size.
   *
   * @throws NullPointerException An argument is null.
   * @throws IllegalArgumentException An argument is out of range.
   * @throws IOException An I/O exception occurred.
   */
  @Validate
  public static void readFully(@NotNull final InputStream stream, @NotNull final byte[] buffer, final long length, final int maxChunkSize)
      throws IOException
  {
    if (length < 0)
      throw new IllegalArgumentException("length=" + length);
    if (buffer.length < length)
      throw new IllegalArgumentException("bufferLen=" + buffer.length + " length" + length);
    if (length > Integer.MAX_VALUE)
      throw new IllegalArgumentException("length=" + length);
    if (maxChunkSize <= 0)
      throw new IllegalArgumentException("maxChunkSize=" + maxChunkSize);

    int totalRemaining = (int) length;
    int totalRead = 0;

    while (totalRemaining > 0)
    {
      // attempt to read chunk or complete length depending on size
      int read = stream.read(buffer, totalRead, (totalRemaining < maxChunkSize ? totalRemaining : maxChunkSize));

      if (read <= 0)
        throw new IOException(String.format(EOF_EXCEPTION_MESSAGE_BYTES, totalRemaining));

      // maintain counters
      totalRead += read;
      totalRemaining -= read;
    }
  }

  /**
   * Blocks until a specified amount of bytes is read from a stream. Returns the data read. Performs multiple reads if the size if bigger
   * than the maximum allowed chunk size.
   *
   * @throws NullPointerException An argument is null.
   * @throws IllegalArgumentException An argument is out of range.
   * @throws IOException An I/O exception occurred.
   */
  @Validate
  public static byte[] readFully(@NotNull final InputStream stream, final long length, final int maxChunkSize)
      throws IOException
  {
    if (length < 0 || length > Integer.MAX_VALUE)
      throw new IllegalArgumentException("length=" + length);

    val buffer = new byte[(int) length];
    readFully(stream, buffer, length, maxChunkSize);
    return buffer;
  }

  /**
   * Blocks until the specified character is encountered and all characters read until then from the UTF8-encoded byte-stream are skipped.
   *
   * @throws NullPointerException An argument is null.
   * @throws IOException An I/O exception occurred.
   */
  public static void skipUntil(final InputStream stream, final char terminator)
      throws IOException
  {
    skipUntil(stream, terminator, CONSTANT.UTF8);
  }

  /**
   * Blocks until the specified character is encountered and all characters read until then from the byte-stream are skipped.
   *
   * @throws NullPointerException An argument is null.
   * @throws IllegalArgumentException An argument is out of range.
   * @throws IOException An I/O exception occurred.
   */
  public static void skipUntil(final InputStream stream, final char terminator, final Charset streamEncoding)
      throws IOException
  {
    while (readCharacter(stream, streamEncoding) != terminator)
    {}
  }

  /**
   * Blocks until the specified string of characters is encountered and all characters read until then from the byte-stream are skipped. The
   * character encoding can be specified.
   *
   * @throws NullPointerException An argument is null.
   * @throws IOException An I/O exception occurred.
   */
  public static void skipUntil(final InputStream stream, final String terminator)
      throws IOException
  {
    skipUntil(stream, terminator, CONSTANT.UTF8);
  }

  /**
   * Blocks until the specified string of characters is encountered and all characters read until then from the UTF8-encoded byte-stream are
   * skipped.
   *
   * @throws NullPointerException An argument is null.
   * @throws IOException An I/O exception occurred.
   */
  @Validate
  public static void skipUntil(@NotNull final InputStream stream, @NotNull final String terminator, @NotNull final Charset streamEncoding)
      throws IOException
  {
    // this is the terminator string
    val terminatorCharBuffer = ByteBuffer.wrap(terminator.getBytes(streamEncoding)).asCharBuffer();

    // buffer holding the latest characters
    CharBuffer tempCharBuffer = CharBuffer.allocate(terminatorCharBuffer.length());

    // read the first batch
    for (int i = 0; i < terminatorCharBuffer.length(); i++)
      tempCharBuffer.put(readCharacter(stream, streamEncoding));

    // compare and read next until arrays hold equal values
    while (!(StringUtils.sequenceEqual(terminatorCharBuffer.array(), tempCharBuffer.array())))
    {
      // read next char
      val ch = readCharacter(stream, streamEncoding);

      // left shift elements in array, throw away the first read
      tempCharBuffer = CharBuffer.wrap(tempCharBuffer.subSequence(1, tempCharBuffer.length())).append(ch);
    }
  }

  /**
   * Blocks until the specified byte is encountered and all bytes read until then are skipped.
   *
   * @throws NullPointerException An argument is null.
   * @throws IOException An I/O exception occurred.
   */
  @Validate
  public static void skipUntil(@NotNull final InputStream stream, final byte terminator)
      throws IOException
  {
    while (true)
    {
      // read next byte
      val read = stream.read();

      // check status
      if (read >= 0)
      {
        if ((byte) read == terminator)
          break;
      } else
        throw new IOException(String.format(EOF_EXCEPTION_MESSAGE_BYTES, 1));
    }
  }

  /**
   * Blocks until the specified byte sequence is encountered and all bytes read until then are skipped.
   *
   * @throws NullPointerException An argument is null.
   * @throws IOException An I/O exception occurred.
   */
  @Validate
  public static void skipUntil(@NotNull final InputStream stream, @NotNull final byte[] terminator)
      throws IOException
  {
    // buffer holding the latest bytes
    val tempBuffer = new byte[terminator.length];

    // read the first batch
    for (int i = 0; i < tempBuffer.length; i++)
    {
      val read = stream.read();

      // check status
      if (read >= 0)
        tempBuffer[i] = (byte) read;
      else
        throw new IOException(String.format(EOF_EXCEPTION_MESSAGE_BYTES, tempBuffer.length - i));
    }

    // compare and read next until arrays hold equal values
    while (!(ByteArrayUtils.sequenceEqual(tempBuffer, terminator)))
    {
      // read next byte
      val read = stream.read();

      // check status
      if (read >= 0)
      {
        // left shift elements in array, throw away the first read
        for (int i = 0; i < tempBuffer.length - 1; i++)
          tempBuffer[i] = tempBuffer[i + 1];

        // set last array element to read char
        tempBuffer[tempBuffer.length - 1] = (byte) read;
      } else
        throw new IOException(String.format(EOF_EXCEPTION_MESSAGE_BYTES, 1));
    }
  }

  /**
   * Blocks until a specified amount of bytes is read from a stream. Throws away the bytes read.
   *
   * @throws NullPointerException An argument is null.
   * @throws IllegalArgumentException An argument is out of range.
   * @throws IOException An I/O exception occurred.
   */
  public static void skipBytes(final InputStream stream, final int length)
      throws IOException
  {
    readFully(stream, length);
  }

  /**
   * Skips a specified number of characters from byte-stream, using UTF8 to convert bytes into characters.
   *
   * @throws NullPointerException An argument is null.
   * @throws IllegalArgumentException An argument is out of range.
   * @throws IOException An I/O exception occurred.
   */
  public static void skipCharacters(final InputStream stream, final int count)
      throws IOException
  {
    skipCharacters(stream, count, CONSTANT.UTF8);
  }

  /**
   * Skips a specified number of characters from the byte-stream, using the specified encoding to convert bytes into characters.
   *
   * @throws NullPointerException An argument is null.
   * @throws IllegalArgumentException An argument is out of range.
   * @throws IOException An I/O exception occurred.
   */
  @Validate
  public static void skipCharacters(final InputStream stream, final int count, @NotNull final Charset streamEncoding)
      throws IOException
  {
    if (count < 0)
      throw new IllegalArgumentException("count");

    for (int i = 0; i < count; i++)
      readCharacter(stream, streamEncoding);
  }

  /**
   * Reads a string from the UTF8-encoded byte-stream until a terminator character is found. The terminator character is thrown away and the
   * stream moves past it.
   *
   * @throws NullPointerException An argument is null.
   * @throws IOException An I/O exception occurred.
   */
  public static String readUntil(final InputStream stream, final char terminator)
      throws IOException
  {
    return readUntil(stream, terminator, CONSTANT.UTF8);
  }

  /**
   * Reads a string from the stream until a terminator character is found. The terminator character is thrown away and the stream moves past
   * it.
   *
   * @throws NullPointerException An argument is null.
   * @throws IOException An I/O exception occurred.
   */
  @Validate
  public static String readUntil(final InputStream stream, final char terminator, @NotNull final Charset streamEncoding)
      throws IOException
  {
    char ch;
    val sb = new StringBuilder(256);

    while ((ch = readCharacter(stream, streamEncoding)) != terminator)
      sb.append(ch);

    return sb.toString();
  }

  /**
   * Reads a string from the UTF8-encoded byte-stream until a terminator string is found. The terminator string is thrown away and the
   * stream moves past it.
   *
   * @throws NullPointerException An argument is null.
   * @throws IOException An I/O exception occurred.
   */
  public static String readUntil(final InputStream stream, final String terminator)
      throws IOException
  {
    return readUntil(stream, terminator, CONSTANT.UTF8);
  }

  /**
   * Reads a string from the byte-stream with the specified encoding until a terminator string is found. The terminator string is thrown
   * away and the stream moves past it.
   *
   * @throws NullPointerException An argument is null.
   * @throws IOException An I/O exception occurred.
   */
  @Validate
  public static String
      readUntil(@NotNull final InputStream stream, @NotNull final String terminator, @NotNull final Charset streamEncoding)
          throws IOException
  {
    val result = new StringBuilder(256);

    // this is the terminator string
    val terminatorCharBuffer = CharBuffer.wrap(terminator.toCharArray());

    // buffer holding the latest characters read
    CharBuffer tempCharBuffer = CharBuffer.allocate(terminatorCharBuffer.length());

    // read the first batch to fill the buffer
    for (int i = 0; i < terminatorCharBuffer.length(); i++)
      tempCharBuffer.put(readCharacter(stream, streamEncoding));

    // compare and read next until arrays hold equal values
    while (!(StringUtils.sequenceEqual(terminatorCharBuffer.array(), tempCharBuffer.array())))
    {
      // read next char
      val ch = readCharacter(stream, streamEncoding);

      // left shift elements in array, throw away the first read
      tempCharBuffer = CharBuffer.wrap(tempCharBuffer.subSequence(1, tempCharBuffer.length())).append(ch);

      // add to result string
      result.append(ch);
    }

    return result.substring(0, result.length() - terminatorCharBuffer.length());
  }

  /**
   * Reads data from the stream until a terminator byte is found. The terminator byte is thrown away and the stream moves past it.
   *
   * @throws NullPointerException An argument is null.
   * @throws IOException An I/O exception occurred.
   */
  @Validate
  public static byte[] readUntil(@NotNull final InputStream stream, final byte terminator)
      throws IOException
  {
    val result = new ByteArrayOutputStream(128);

    while (true)
    {
      // read next byte
      val read = stream.read();

      // check status
      if (read >= 0)
      {
        val bt = (byte) read;

        if (bt == terminator)
          break;

        result.write(bt);
      } else
        throw new IOException(String.format(EOF_EXCEPTION_MESSAGE_BYTES, 1));
    }

    return result.toByteArray();
  }

  /**
   * Reads data from the byte-stream until a terminator string is found. The terminator string is thrown away and the stream moves past it.
   *
   * @throws NullPointerException An argument is null.
   * @throws IOException An I/O exception occurred.
   */
  @Validate
  public static byte[] readUntil(@NotNull final InputStream stream, @NotNull final byte[] terminator)
      throws IOException
  {
    val result = new ByteArrayOutputStream(128);

    // buffer holding the latest bytes
    val tempBuffer = new byte[terminator.length];

    // read the first batch
    for (int i = 0; i < tempBuffer.length; i++)
    {
      int read = stream.read();

      // check status
      if (read >= 0)
        tempBuffer[i] = (byte) read;
      else
        throw new IOException(String.format(EOF_EXCEPTION_MESSAGE_BYTES, tempBuffer.length - i));
    }
    result.write(tempBuffer, 0, tempBuffer.length);

    // compare and read next until arrays hold equal values
    while (!(ByteArrayUtils.sequenceEqual(tempBuffer, terminator)))
    {
      // read next byte
      val read = stream.read();

      // check status
      if (read >= 0)
      {
        val bt = (byte) read;

        // shift elements in array to the left, throw away the first read
        for (int i = 0; i < tempBuffer.length - 1; i++)
          tempBuffer[i] = tempBuffer[i + 1];

        // set last array element to read char
        tempBuffer[tempBuffer.length - 1] = bt;

        result.write(bt);
      } else
        throw new IOException(String.format(EOF_EXCEPTION_MESSAGE_BYTES, 1));
    }

    // remove terminator
    return ByteArrayUtils.resize(result.toByteArray(), result.size() - terminator.length);
  }

  /**
   * Reads a string from the byte-stream with the specified encoding until a terminator string is found. The terminator string is thrown
   * away and the stream moves past it.
   *
   * @throws NullPointerException An argument is null.
   * @throws IOException An I/O exception occurred.
   */
  @Validate
  public static String readUntil(InputStream stream, byte[] terminator, @NotNull final Charset streamEncoding)
      throws IOException
  {
    val ba = readUntil(stream, terminator);
    val result = streamEncoding.decode(ByteBuffer.wrap(ba));
    return result.toString();
  }

  /**
   * Reads the next characters, if any, from a <param name="stream"/>,decoding bytes using the UTF8 decoder.
   *
   * @throws NullPointerException An argument is null.
   * @throws IllegalArgumentException An argument is out of range.
   * @throws IOException An I/O exception occurred.
   */
  public static char[] readCharacters(final InputStream stream, final int count)
      throws IOException
  {
    return readCharacters(stream, CONSTANT.UTF8, count);
  }

  /**
   * Reads the next characters, if any, from a <param name="stream"/>, decoding bytes using the specified decoder.
   *
   * @throws NullPointerException An argument is null.
   * @throws IllegalArgumentException An argument is out of range.
   * @throws IOException An I/O exception occurred.
   */
  public static char[] readCharacters(final InputStream stream, final Charset streamEncoding, final int count)
      throws IOException
  {
    if (count < 0)
      throw new IllegalArgumentException("count");

    char[] result = new char[count];
    int i = 0;

    try
    {
      for (i = 0; i < count; i++)
        result[i] = readCharacter(stream, streamEncoding);
    }
    catch(IOException e)
    {
      // repackage exception to mention actual number of characters expected
      if (e.getMessage().contains(CONSTANT.EOF))
        throw new IOException(String.format(EOF_EXCEPTION_MESSAGE_CHARACTERS, count - i));

      throw e;
    }
    return result;
  }

  /**
   * Reads the next character, if any, from a UTF8-encoded byte-stream,decoding bytes UTF8 format.
   *
   * @throws NullPointerException An argument is null.
   * @throws IOException An I/O exception occurred.
   */
  public static char readCharacter(final InputStream stream)
      throws IOException
  {
    return readCharacter(stream, CONSTANT.UTF8);
  }

  /**
   * Reads the next character from a byte-stream, if any, decoding bytes into the specified encoding's decoder e.g.
   * UTF8Encoding.GetDecoder().
   *
   * @throws NullPointerException An argument is null.
   * @throws IOException An I/O exception occurred.
   */
  @Validate
  public static char readCharacter(@NotNull final InputStream stream, @NotNull final Charset streamEncoding)
      throws IOException
  {
    val byteBuffer = ByteBuffer.allocate(8);

    CharBuffer chars;
    do
    {
      int b = stream.read();

      if (b < 0)
        throw new IOException(String.format(EOF_EXCEPTION_MESSAGE_CHARACTERS, 1));

      byteBuffer.put((byte) b);

      // attempt to decode byte -> char, returns 0 if failed
      chars = streamEncoding.decode(byteBuffer);
    }
    while (chars.length() == 0);

    return chars.get();
  }

  /**
   * Reads all characters from a stream, returning a string.
   *
   * @throws NullPointerException An argument is null.
   * @throws IOException An I/O exception occurred.
   */
  @Validate
  public static String readAllCharacters(@NotNull final Reader textStream)
      throws IOException
  {
    // create a 4KB buffer to read the file
    CharArrayWriter writer = new CharArrayWriter();
    final char[] bytes = new char[4096];

    int bytesRead;
    while ((bytesRead = textStream.read(bytes)) > -1)
    {
      writer.write(bytes, 0, bytesRead);
    }
    writer.flush();

    return writer.toString();
  }

  /**
   * Skips characters in the stream until the specified character is read
   *
   * @throws NullPointerException An argument is null.
   * @throws IOException An I/O exception occurred.
   */
  public static void skipUntil(final Reader textStream, final char terminator)
      throws IOException
  {
    while (readCharacter(textStream) != terminator)
    {}
  }

  /**
   * Skips characters in the stream until the specified string is read
   *
   * @throws NullPointerException An argument is null.
   * @throws IOException An I/O exception occurred.
   */
  public static void skipUntil(final Reader textStream, final String terminator)
      throws IOException
  {
    readUntil(textStream, terminator);
  }

  /**
   * Reads a specified number of characters from the stream.
   *
   * @throws NullPointerException An argument is null.
   * @throws IllegalArgumentException An argument is out of range.
   * @throws IOException An I/O exception occurred.
   */
  public static String readFully(final Reader textStream, final int count)
      throws IOException
  {
    if (count < 0)
      throw new IllegalArgumentException("count=" + count);

    val sb = new StringBuilder(256);

    for (int i = 0; i < count; i++)
      sb.append(readCharacter(textStream));

    return sb.toString();
  }

  /**
   * Skips a specified number of characters from the stream
   *
   * @throws NullPointerException An argument is null.
   * @throws IllegalArgumentException An argument is out of range.
   * @throws IOException An I/O exception occurred.
   */
  public static void skipCharacters(final Reader textStream, final int count)
      throws IOException
  {
    if (count < 0)
      throw new IllegalArgumentException("count=" + count);

    for (int i = 0; i < count; i++)
      readCharacter(textStream);
  }

  /**
   * Reads a string from the stream until a terminator character is found. The terminator character is thrown away and the stream moves past
   * it.
   *
   * @throws NullPointerException An argument is null.
   * @throws IOException An I/O exception occurred.
   */
  public static String readUntil(final Reader textStream, final char terminator)
      throws IOException
  {
    char ch;
    val sb = new StringBuilder(256);

    while ((ch = readCharacter(textStream)) != terminator)
      sb.append(ch);

    return sb.toString();
  }

  /**
   * Reads a string from the stream until a terminator string is found. The terminator string is thrown away and the stream moves past it.
   *
   * @throws NullPointerException An argument is null.
   * @throws IOException An I/O exception occurred.
   */
  @Validate
  public static String readUntil(final Reader textStream, @NotNull final String terminator)
      throws IOException
  {
    val result = new StringBuilder(256);

    // this is the terminator string
    val terminatorChars = terminator.toCharArray();

    // buffer holding the latest characters
    val tempBuffer = new char[terminatorChars.length];

    // read the first batch
    for (int i = 0; i < tempBuffer.length; i++)
    {
      tempBuffer[i] = readCharacter(textStream);
      result.append(tempBuffer[i]);
    }

    // compare and read next until arrays hold equal values
    while (!(StringUtils.sequenceEqual(terminatorChars, tempBuffer)))
    {
      // read next char
      val ch = readCharacter(textStream);

      // left shift elements in array, throw away the first read
      for (int i = 0; i < tempBuffer.length - 1; i++)
        tempBuffer[i] = tempBuffer[i + 1];

      // set last array element to read char
      tempBuffer[tempBuffer.length - 1] = ch;

      // add to result string
      result.append(ch);
    }

    return result.substring(0, result.length() - terminator.length());
  }

  /**
   * Reads a string from the stream until a terminator character is found. The terminator character is not read (but peeked) therefore the
   * stream does not move past it.
   *
   * @throws NullPointerException An argument is null.
   * @throws IllegalArgumentException A stream was passed that does not support peeking (mark/reset)
   * @throws IOException An I/O exception occurred.
   */
  public static String readUntilPeeking(final Reader textStream, final char terminator)
      throws IOException
  {
    val result = new StringBuilder(256);
    while (peekCharacter(textStream) != terminator)
      result.append(readCharacter(textStream));

    return result.toString();
  }

  /**
   * Reads the next stream character, if any.
   *
   * @throws NullPointerException An argument is null.
   * @throws IOException An I/O exception occurred.
   */
  @Validate
  public static char readCharacter(@NotNull final Reader textStream)
      throws IOException
  {
    val ch = textStream.read();

    if (ch < 0)
      throw new IOException(String.format(EOF_EXCEPTION_MESSAGE_CHARACTERS, 1));

    return (char) ch;
  }

  /**
   * Reads the next stream characters, if any.
   *
   * @throws NullPointerException An argument is null.
   * @throws IllegalArgumentException An argument is out of range.
   * @throws IOException An I/O exception occurred.
   */
  public static char[] readCharacters(final Reader textStream, final int count)
      throws IOException
  {
    if (count < 0)
      throw new IllegalArgumentException("count=" + count);

    val result = new char[count];
    int i = 0;

    try
    {
      for (i = 0; i < count; i++)
        result[i] = readCharacter(textStream);
    }
    catch(IOException e)
    {
      // repackage exception to mention actual number of characters expected
      if (e.getMessage().contains(CONSTANT.EOF))
        throw new IOException(String.format(EOF_EXCEPTION_MESSAGE_CHARACTERS, count - i));

      throw e;
    }
    return result;
  }

  /**
   * Peeks the next stream character, if any.
   *
   * @throws NullPointerException An argument is null.
   * @throws IllegalArgumentException A stream was passed that does not support peeking (mark/reset)
   * @throws IOException An I/O exception occurred.
   */
  @Validate
  public static char peekCharacter(@NotNull final Reader textStream)
      throws IOException
  {
    if (!textStream.markSupported())
      throw new IllegalArgumentException("Peeking is not possible because mark()/reset() are not supported!");

    textStream.mark(1);

    val ch = textStream.read();

    textStream.reset();

    if (ch < 0)
      throw new IOException(String.format(EOF_EXCEPTION_MESSAGE_CHARACTERS, 1));

    return (char) ch;
  }

  private StreamUtils()
  {
  }
}
TOP

Related Classes of propel.core.utils.StreamUtils

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.