All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.freeutils.util.Streams Maven / Gradle / Ivy

The newest version!
/*
 *  Copyright © 2003-2024 Amichai Rothman
 *
 *  This file is part of JElementary - the Java Elementary Utilities package.
 *
 *  JElementary is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  JElementary 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with JElementary.  If not, see .
 *
 *  For additional info see https://www.freeutils.net/source/jelementary/
 */

package net.freeutils.util;

import java.io.*;

/**
 * The Streams class contains utility methods related to Streams and I/O.
 */
public class Streams {

    /**
     * Private constructor to avoid external instantiation.
     */
    private Streams() {}

    /**
     * Reads bytes from a stream. The number of bytes read is no less
     * than the given minimum and no more than the given maximum.
     *
     * @param in the input stream to read from
     * @param b the byte array to read into
     * @param off the offset within the byte array to start reading into
     * @param min the minimum number of bytes to read
     * @param max the maximum number of bytes to read
     * @return the number of bytes read
     * @throws IOException if an error occurs or there are less
     *         than the minimum number of bytes in the stream
     */
    public static int read(InputStream in, byte[] b, int off, int min, int max) throws IOException {
        int total = 0;
        while (total < min) {
            int read = in.read(b, off + total, max - total);
            if (read < 0)
                throw new IOException("Unexpected end of stream");
            total += read;
        }
        return total;
    }

    /**
     * Transfers data from an input stream to an output stream.
     *
     * @param in the input stream to read from
     * @param out the output stream to write to
     * @param maxLength maximum number of bytes to read;
     *        if negative, read until end of stream
     * @param closeIn if true, the input stream is closed by this method
     * @param closeOut if true, the output stream is closed by this method
     * @return the number of bytes transferred
     * @throws IOException if an error occurs
     */
    public static long transfer(InputStream in, OutputStream out, long maxLength, boolean closeIn, boolean closeOut) throws IOException {
        long len = maxLength;
        byte[] buf = new byte[16384];
        try {
            while (len != 0) {
                int count = len < 0 || buf.length < len ? buf.length : (int)len;
                count = in.read(buf, 0, count);
                if (count == -1)
                    break;
                out.write(buf, 0, count);
                len -= count;
            }
        } finally {
            if (closeIn)
                Streams.closeSilently(in);
            if (closeOut)
                Streams.closeSilently(out);
        }
        return maxLength - len;
    }

    /**
     * Transfers the entire content of one stream into another stream.
     *
     * @param in the input stream to read from
     * @param out the output stream to write to
     * @param closeIn if true, the input stream is closed by this method
     * @param closeOut if true, the output stream is closed by this method
     * @throws IOException if an error occurs
     */
    public static void transfer(InputStream in, OutputStream out, boolean closeIn, boolean closeOut) throws IOException {
        transfer(in, out, -1, closeIn, closeOut);
    }

    /**
     * Transfers the entire content of a stream into a file.
     *
     * @param in the input stream to read from
     * @param out the output file to write to
     * @param closeIn if true, the input stream is closed by this method
     * @throws IOException if an error occurs
     */
    public static void transfer(InputStream in, File out, boolean closeIn) throws IOException {
        transfer(in, new FileOutputStream(out), -1, closeIn, true);
    }

    /**
     * Transfers data from an input stream to an output stream.
     * 

* Neither stream is closed by this method. * * @param in the input stream to transfer from * @param out the output stream to transfer to * @param len the number of bytes to transfer. If negative, the * entire contents of the input stream are transferred. * @throws EOFException if the input stream ends before the * requested number of bytes have been read * @throws IOException if an IO error occurs */ public static void transfer(InputStream in, OutputStream out, long len) throws IOException { if (transfer(in, out, len, false, false) < len) throw new EOFException("unexpected end of stream"); } /** * Closes all given Closeables in their natural order, * silently ignoring nulls and thrown exceptions. * * @param closeables the Closeables to close */ public static void closeSilently(Closeable... closeables) { for (Closeable c : closeables) { try { if (c != null) c.close(); } catch (IOException ignore) {} } } /** * Reads the contents of a stream as a String in the given encoding. * The stream is closed by this method. * * @param in the stream to read * @param enc the encoding to use * @return the file contents as a String in the given encoding * @throws IOException if an error occurs */ public static String readString(InputStream in, String enc) throws IOException { return Streams.readString(in, enc, -1); } /** * Reads the contents of a stream as a String in the given encoding. * The stream is closed by this method. * * @param in the stream to read * @param enc the encoding to use * @param maxLength maximum number of bytes to read; * if negative, read until end of stream * @return the file contents as a String in the given encoding * @throws IOException if an error occurs */ public static String readString(InputStream in, String enc, int maxLength) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(4096); Streams.transfer(in, out, maxLength, true, false); return out.toString(enc); } /** * Reads the contents of a stream as an array of text lines in the given encoding. * * @param in the stream from which the lines are read * @param enc the encoding to use * @param lenient if true, either an LF or a CRLF can end the line; * if false, only a CRLF can end the line * @return the lines read from the stream * @throws IOException if an error occurs */ public static String[] readLines(InputStream in, String enc, boolean lenient) throws IOException { return Strings.splitLines(readString(in, enc), lenient); } /** * Reads the contents of a stream as a byte array. * The stream is closed by this method. * * @param in the stream to read * @return the file contents as a byte array * @throws IOException if an error occurs */ public static byte[] readBytes(InputStream in) throws IOException { return Streams.readBytes(in, -1); } /** * Reads the contents of a stream as a byte array. * The stream is closed by this method. * * @param in the stream to read * @param maxLength maximum number of bytes to read; * if negative, read until end of stream * @return the file contents as a byte array * @throws IOException if an error occurs */ public static byte[] readBytes(InputStream in, int maxLength) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(4096); Streams.transfer(in, out, maxLength, true, false); return out.toByteArray(); } /** * Reads the token starting at the current stream position and ending at * the first occurrence of the given delimiter byte, in the given encoding. * * @param in the stream from which the token is read * @param delim the byte value which marks the end of the token, * or -1 if the token ends at the end of the stream * @param enc a character-encoding name * @param maxLength the maximum length (in bytes) to read * @return the read token, excluding the delimiter * @throws UnsupportedEncodingException if the encoding is not supported * @throws EOFException if the stream end is reached before the delimiter * is found (and it is not -1) * @throws IOException if an IO error occurs, or the maximum length is * reached before the token end is reached */ public static String readToken(InputStream in, int delim, String enc, int maxLength) throws IOException { // note: we avoid using a ByteArrayOutputStream here because it // suffers the overhead of synchronization for each byte written int buflen = maxLength < 512 ? maxLength : 512; // start with less byte[] buf = new byte[buflen]; int count = 0; int c; while ((c = in.read()) != -1 && c != delim) { if (count == buflen) { // expand buffer if (count == maxLength) throw new IOException("token too large (" + count + ")"); buflen = maxLength < 2 * buflen ? maxLength : 2 * buflen; byte[] expanded = new byte[buflen]; System.arraycopy(buf, 0, expanded, 0, count); buf = expanded; } buf[count++] = (byte)c; } if (c == -1 && delim != -1) throw new EOFException("unexpected end of stream"); return new String(buf, 0, count, enc); } /** * Reads the string starting at the current stream position and ending * at the first occurrence of an end-of-line sequence, in the given * encoding. * * @param in the stream from which the line is read * @param enc the encoding to use * @param lenient if true, either an LF or a CRLF can end the line; * if false, only a CRLF can end the line * @return the read string, excluding the end-of-line characters * @throws EOFException if the stream end is reached before an * end-of-line sequence is found * @throws IOException if an IO error occurs, or the line is * longer than 8192 bytes * @see #readToken(InputStream, int, String, int) */ public static String readLine(InputStream in, String enc, boolean lenient) throws IOException { String s = readToken(in, '\n', enc, 8192); if (lenient) { return s.length() > 0 && s.charAt(s.length() - 1) == '\r' ? s.substring(0, s.length() - 1) : s; } while (s.isEmpty() || s.charAt(s.length() - 1) != '\r') { s += readToken(in, '\n', enc, 8192); if (s.length() > 8192) throw new IOException("token too large"); } return s.substring(0, s.length() - 1); } /** * Reads the ISO-8859-1 encoded string starting at the current stream * position and ending at the first occurrence of an end-of-line * sequence. * * @param in the stream from which the line is read * @param lenient if true, either an LF or a CRLF can end the line; * if false, only a CRLF can end the line * @return the read string, excluding the end-of-line characters * @throws EOFException if the stream end is reached before * an end-of-line sequence is found * @throws IOException if an IO error occurs, or the line is * longer than 8192 bytes * @see #readToken(InputStream, int, String, int) */ public static String readLine(InputStream in, boolean lenient) throws IOException { return readLine(in, "ISO8859_1", lenient); } /** * Reads the ISO-8859-1 encoded string starting at the current stream * position and ending at the first occurrence of an end-of-line * sequence (LF or CRLF). * * @param in the stream from which the line is read * @return the read string, excluding the end-of-line characters * @throws EOFException if the stream end is reached before * an end-of-line sequence is found * @throws IOException if an IO error occurs, or the line is * longer than 8192 bytes * @see #readToken(InputStream, int, String, int) */ public static String readLine(InputStream in) throws IOException { return readLine(in, "ISO8859_1", true); } /** * Reads the token starting at the current stream position and ending at * the first occurrence of the given delimiter byte. * * @param in the stream from which the token is read * @param delim the byte value which marks the end of the token, * or -1 if the token ends at the end of the stream * @param buf the buffer into which the line is written * @param off the offset within buf where writing begins * @return the number of bytes read (including the delimiter, if exists) * @throws UnsupportedEncodingException if the encoding is not supported * @throws EOFException if the stream end is reached before the delimiter * is found (and it is not -1) * @throws IOException if an IO error occurs * @throws IndexOutOfBoundsException if the buffer is full before the * token end is reached */ public static int readTokenBytes(InputStream in, int delim, byte[] buf, int off) throws IOException { int pos = off; int c; while ((c = in.read()) != -1) { if (pos == buf.length) throw new IndexOutOfBoundsException("token too large"); buf[pos++] = (byte)c; if (c == delim) break; } if (c == -1 && delim != -1) throw new EOFException("unexpected end of stream"); return pos - off; } /** * Reads bytes from a stream starting at the current stream * position and ending at the first occurrence of the CRLF sequence. * * @param in the stream from which the line is read * @param buf the buffer into which the line is written * @return the number of bytes read (including the CRLF) * @throws EOFException if the stream end is reached * before a CRLF sequence is found * @throws IOException if an IO error occurs * @throws IndexOutOfBoundsException if the buffer is full before a * CRLF is reached * @see #readTokenBytes(InputStream, int, byte[], int) */ public static int readLineBytes(InputStream in, byte[] buf) throws IOException { int pos = 0; do { pos += readTokenBytes(in, '\n', buf, pos); } while (pos < 2 || buf[pos - 2] != '\r'); return pos; } /** * Serializes the given object to a stream. * * @param out the stream to serialize to * @param obj the object to serialize * @param the object type * @param close specifies whether the stream should be closed after writing * @throws IOException if an error occurs */ public static void serialize(OutputStream out, T obj, boolean close) throws IOException { BufferedOutputStream bos = null; ObjectOutputStream oos = null; try { bos = new BufferedOutputStream(out); oos = new ObjectOutputStream(bos); oos.writeObject(obj); } finally { Streams.closeSilently(oos, bos, close ? out : null); } } /** * Serializes the given object to its byte representation. *

* The object can be reconstructed using the {@link #deserialize} method. * * @param obj the object to serialize * @param the object type * @return the serialized bytes * @throws IOException if an error occurs */ public static byte[] serialize(T obj) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); serialize(out, obj, false); return out.toByteArray(); } /** * Deserializes an object from a stream. * * @param in the stream to deserialize from * @param close specifies whether the stream should be closed after reading * @param the object type * @return the deserialized object * @throws IOException if an error occurs */ @SuppressWarnings("unchecked") public static T deserialize(InputStream in, boolean close) throws IOException { BufferedInputStream bis = null; ObjectInputStream ois = null; try { bis = new BufferedInputStream(in); ois = new ObjectInputStream(bis); return (T)ois.readObject(); } catch (ClassNotFoundException cnfe) { throw new IOException("can't read object from stream", cnfe); } finally { Streams.closeSilently(ois, bis, close ? in : null); } } /** * De-serializes an object from its byte representation. *

* The serialized bytes should have been obtained using * the {@link #serialize} method. * * @param bytes the serialized bytes * @param the object type * @return the de-serialized object * @throws IOException if an error occurs */ public static T deserialize(byte[] bytes) throws IOException { return deserialize(new ByteArrayInputStream(bytes), false); } /** * Returns an InputStream whose content is the concatenation * of all the given streams' content, in order. * * @param in the input streams from which the data is read (in order) * @return an InputStream whose content is read from the given streams */ public static InputStream concat(final InputStream... in) { return new InputStream() { int i; @Override public int read() throws IOException { for ( ; i < in.length; i++) { int b = in[i].read(); if (b > -1) return b; } return -1; } @Override public int read(byte[] b, int off, int len) throws IOException { for ( ; i < in.length; i++) { int count = in[i].read(b, off, len); if (count > -1) return count; } return -1; } @Override public int available() throws IOException { return i < in.length ? in[i].available() : 0; } @Override public void close() throws IOException { closeSilently(in); } }; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy