public class CodeFlowError extends RuntimeException {

    static final String FLUENT_SHOULD_PREVENT = "%s This should error should have been prevented by the fluent IoStream builder constraints."
            + " Please create report this issue at https://github.com/fralalonde/iostream/issues";

    /**
     * <p>
     * Constructor for CodeFlowError.
     * </p>
     *
     * @param message a {@link java.lang.String} object.
     */
    public CodeFlowError(String message) {
        super(String.format(FLUENT_SHOULD_PREVENT, message));
    }

    /**
     * <p>
     * Constructor for CodeFlowError.
     * </p>
     *
     * @param format a {@link java.lang.String} format template.
     * @param arg    an object to format
     */
    public CodeFlowError(String format, Object arg) {
        super(String.format(FLUENT_SHOULD_PREVENT, String.format(format, arg)));
    }

} public class Base64Filter implements FilterFactory {

    @Override
    public InputStream filterInput(InputStream input) {
        return Base64.getDecoder().wrap(input);
    }

    @Override
    public OutputStream filterOutput(OutputStream output) {
        return Base64.getEncoder().wrap(output);
    }

} @RequiredArgsConstructor
public class CipherFilter implements FilterFactory {

    @NonNull
    final Cipher cipher;

    @Override
    public InputStream filterInput(InputStream input) {
        return new CipherInputStream(input, cipher);
    }

    @Override
    public OutputStream filterOutput(OutputStream output) {
        return new CipherOutputStream(output, cipher);
    }

} public interface FilterFactory {

    /**
     * @param input the input stream to filter
     * @return the filtered input stream
     * @throws IOException if the filter could not be inserted
     */
    InputStream filterInput(InputStream input) throws IOException;

    /**
     * @param output the output stream to filter
     * @return the filtered output stream
     * @throws IOException if the filter could not be inserted
     */
    OutputStream filterOutput(OutputStream output) throws IOException;

} @RequiredArgsConstructor
public class GzipFilter implements FilterFactory {

    final int gzipBufferSize;

    @Override
    public InputStream filterInput(InputStream input) throws IOException {
        switch (gzipBufferSize) {
        case Resource.DEFAULT_BUFFER_SIZE:
            return new GZIPInputStream(input);
        default:
            return new GZIPInputStream(input, gzipBufferSize);
        }
    }

    @Override
    public OutputStream filterOutput(OutputStream output) throws IOException {
        switch (gzipBufferSize) {
        case Resource.DEFAULT_BUFFER_SIZE:
            return new GZIPOutputStream(output);
        default:
            return new GZIPOutputStream(output, gzipBufferSize);
        }
    }

} public interface ByteIn<T> {

    InputStreamOf<T> inputStream() throws IOException;

    BufferedInputOf<T> bufferedInputStream(int bufferSize) throws IOException;

    BufferedInputOf<T> bufferedInputStream() throws IOException;

    ZipInputOf<T> zipInputStream(Charset charset, int bufferSize) throws IOException;

    ZipInputOf<T> zipInputStream(String charsetName, int bufferSize) throws IOException;

    ZipInputOf<T> zipInputStream(Charset charset) throws IOException;

    ZipInputOf<T> zipInputStream(String charsetName) throws IOException;

    ZipInputOf<T> zipInputStream(int bufferSize) throws IOException;

    ZipInputOf<T> zipInputStream() throws IOException;

    DataInputOf<T> dataInputStream(int bufferSize) throws IOException;

    DataInputOf<T> dataInputStream() throws IOException;

    /**
     * Build a buffered object input stream. NOTE: Unlike other input streams,
     * closing an ObjectInputStream does NOT close the underlying stream.
     * 
     * @param bufferSize the size of the read buffer
     * @return an ObjectInputOf
     * @throws IOException if something bad happens
     */
    ObjectInputOf<T> objectInputStream(int bufferSize) throws IOException;

    /**
     * Build a unbuffered object input stream. NOTE: Unlike other input streams,
     * closing an ObjectInputStream does NOT close the underlying stream.
     * 
     * @return an ObjectInputOf
     * @throws IOException if something bad happens
     */
    ObjectInputOf<T> objectInputStream() throws IOException;

    // UNBUFFERED

    /**
     * Build an unbuffered reader using the specified charset.
     * 
     * @param charset The ;@link Charset} to use
     * @return A ;@link ReaderOf} proxy extending the ;@link Reader} class
     * @throws IOException If the reader can not be built
     */
    ReaderOf<T> reader(Charset charset) throws IOException;

    /**
     * Build an unbuffered reader using the specified charset name.
     * 
     * @param charsetName The name of the ;@link Charset} to use
     * @return A ;@link ReaderOf} proxy extending the ;@link Reader} class
     * @throws IOException If the reader can not be built
     */
    ReaderOf<T> reader(String charsetName) throws IOException;

    // BUFFERED

    /**
     * Build a buffered reader using the specified charset and the specified buffer
     * size.
     * 
     * @param charset    The ;@link Charset} to use
     * @param bufferSize The size of the buffer to use
     * @return A ;@link ReaderOf} proxy extending the ;@link Reader} class
     * @throws IOException If the reader can not be built
     */
    BufferedReaderOf<T> bufferedReader(Charset charset, int bufferSize) throws IOException;

    /**
     * Build a buffered ;@link Reader} using the specified charset and the buffer
     * size.
     * 
     * @param charset The ;@link Charset} to use
     * @return A ;@link ReaderOf} proxy extending the ;@link Reader} class
     * @throws IOException If the reader can not be built
     */
    BufferedReaderOf<T> bufferedReader(Charset charset) throws IOException;

    /**
     * Build an buffered ;@link Reader} using the specified charset name and the
     * buffer size.
     * 
     * @param charsetName The name of the ;@link Charset} to use
     * @return A ;@link ReaderOf} proxy extending the ;@link Reader} class
     * @throws IOException If the reader can not be built
     */
    BufferedReaderOf<T> bufferedReader(String charsetName) throws IOException;

} public interface ByteOut<T> {

    OutputStreamOf<T> outputStream() throws IOException;

    BufferedOutputOf<T> bufferedOutputStream(int bufferSize) throws IOException;

    BufferedOutputOf<T> bufferedOutputStream() throws IOException;

    ZipOutputOf<T> zipOutputStream(Charset charset, int bufferSize) throws IOException;

    ZipOutputOf<T> zipOutputStream(String charsetName, int bufferSize) throws IOException;

    ZipOutputOf<T> zipOutputStream(Charset charset) throws IOException;

    ZipOutputOf<T> zipOutputStream(String charsetName) throws IOException;

    ZipOutputOf<T> zipOutputStream(int bufferSize) throws IOException;

    ZipOutputOf<T> zipOutputStream() throws IOException;

    DataOutputOf<T> dataOutputStream(int bufferSize) throws IOException;

    DataOutputOf<T> dataOutputStream() throws IOException;

    ObjectOutputOf<T> objectOutputStream(int bufferSize) throws IOException;

    ObjectOutputOf<T> objectOutputStream() throws IOException;

    WriterOf<T> writer(Charset charset) throws IOException;

    WriterOf<T> writer(String charsetName) throws IOException;

    // BUFFERED

    BufferedWriterOf<T> bufferedWriter(Charset charset, int bufferSize) throws IOException;

    BufferedWriterOf<T> bufferedWriter(Charset charset) throws IOException;

    BufferedWriterOf<T> bufferedWriter(String charsetName) throws IOException;

    // PRINT

    PrintWriterOf<T> printWriter(Charset charset, int bufferSize) throws IOException;

    PrintWriterOf<T> printWriter(String charsetName, int bufferSize) throws IOException;

    PrintWriterOf<T> printWriter(Charset charset) throws IOException;

    PrintWriterOf<T> printWriter(String charsetName) throws IOException;

    // PRINT AUTOFLUSH

    PrintWriterOf<T> printWriter(Charset charset, int bufferSize, boolean autoflush) throws IOException;

    PrintWriterOf<T> printWriter(String charsetName, int bufferSize, boolean autoflush) throws IOException;

    PrintWriterOf<T> printWriter(Charset charset, boolean autoflush) throws IOException;

    PrintWriterOf<T> printWriter(String charsetName, boolean autoflush) throws IOException;

} public interface CharIn<T> {

    ReaderOf<T> reader() throws IOException;

    BufferedReaderOf<T> bufferedReader(int bufferSize) throws IOException;

    BufferedReaderOf<T> bufferedReader() throws IOException;

} public interface CharOut<T> {

    WriterOf<T> writer() throws IOException;

    BufferedWriterOf<T> bufferedWriter(int bufferSize) throws IOException;

    BufferedWriterOf<T> bufferedWriter() throws IOException;

    PrintWriterOf<T> printWriter(int bufferSize) throws IOException;

    PrintWriterOf<T> printWriter() throws IOException;

    PrintWriterOf<T> printWriter(int bufferSize, boolean autoflush) throws IOException;

    PrintWriterOf<T> printWriter(boolean autoflush) throws IOException;

} public interface Filter<SAME> {

    /**
     * Apply generic filter to the stream.
     * 
     * @param filter the filter to apply
     * @return the continuation of the IoStream channel builder.
     */
    SAME filter(FilterFactory filter);

    /**
     * Apply GZip filter to the stream.
     * 
     * @return the continuation of the IoStream channel builder.
     */
    SAME gzip();

    /**
     * Apply GZip filter to the stream.
     * 
     * @param bufferSize size of the gzip buffer
     * @return the continuation of the IoStream channel builder.
     */
    SAME gzip(int bufferSize);

    /**
     * Apply {@link Base64} filter to the stream.
     * 
     * @return the continuation of the IoStream channel builder.
     */
    SAME base64();

    /**
     * Apply {@link Cipher} filter to the stream.
     * 
     * @param cipher the cipher to use. cipher must be initialized before the stream
     *               is used.
     * @return the continuation of the IoStream channel builder.
     */
    SAME cipher(Cipher cipher);

} public class BufferedInputOf<T> extends BufferedInputStream implements WrapperOf<T> {

    final Resource<T> closer;

    /**
     * <p>
     * Constructor for BufferedInputOf.
     * </p>
     *
     * @param cl a {@link ca.rbon.iostream.resource.Resource} object.
     * @param is a {@link java.io.InputStream} object.
     */
    public BufferedInputOf(Resource<T> cl, InputStream is) {
        super(is);
        closer = cl;
    }

    /**
     * <p>
     * Constructor for BufferedInputOf.
     * </p>
     *
     * @param cl         a {@link ca.rbon.iostream.resource.Resource} object.
     * @param is         a {@link java.io.InputStream} object.
     * @param bufferSize a int.
     */
    public BufferedInputOf(Resource<T> cl, InputStream is, int bufferSize) {
        super(is, bufferSize);
        closer = cl;
    }

    /** {@inheritDoc} */
    @Override
    public T getInner() throws IOException {
        return closer.getResource();
    }

    /**
     * Create an IntStream from this InputStream's bytes. An IOException may be
     * thrown while processing the IntStream. Consuming the IntStream entirely does
     * not close this InputStream.
     * 
     * @return an IntStream
     */
    public IntStream intStream() {
        return StreamInputAdapter.toIntStream(this);
    }

    /**
     * Use the BufferedInputStream inline and return the inner resource.
     * @param operation The operation to apply.
     * @return The inner resource.
     * @throws IOException if the passed in closure throws
     */
    public T with(BiConsumer<BufferedInputOf<T>, T> operation) throws IOException {
        operation.accept(this, getInner());
        return getInner();
    }
} public class BufferedOutputOf<T> extends BufferedOutputStream implements WrapperOf<T> {

    final Resource<T> closer;

    /**
     * <p>
     * Constructor for BufferedOutputOf.
     * </p>
     *
     * @param cl a {@link ca.rbon.iostream.resource.Resource} object.
     * @param os a {@link java.io.OutputStream} object.
     */
    public BufferedOutputOf(Resource<T> cl, OutputStream os) {
        super(os);
        closer = cl;
    }

    /**
     * <p>
     * Constructor for BufferedOutputOf.
     * </p>
     *
     * @param cl         a {@link ca.rbon.iostream.resource.Resource} object.
     * @param os         a {@link java.io.OutputStream} object.
     * @param bufferSize a int.
     */
    public BufferedOutputOf(Resource<T> cl, OutputStream os, int bufferSize) {
        super(os, bufferSize);
        closer = cl;
    }

    /** {@inheritDoc} */
    @Override
    public T getInner() throws IOException {
        return closer.getResource();
    }

    /**
     * Use the BufferedOutputStream inline and return the inner resource.
     * @param operation The operation to apply.
     * @return The inner resource.
     * @throws IOException if the passed in closure throws
     */
    public T with(BiConsumer<BufferedOutputOf<T>, T> operation) throws IOException {
        operation.accept(this, getInner());
        return getInner();
    }
} public class BufferedReaderOf<T> extends BufferedReader implements WrapperOf<T> {

    final Resource<T> closer;

    /**
     * <p>
     * Constructor for BufferedReaderOf.
     * </p>
     *
     * @param cl a {@link ca.rbon.iostream.resource.Resource} object.
     * @param r  a {@link java.io.Reader} object.
     */
    public BufferedReaderOf(Resource<T> cl, Reader r) {
        super(r);
        closer = cl;
    }

    /** {@inheritDoc} */
    @Override
    public T getInner() throws IOException {
        return closer.getResource();
    }

    /**
     * Use the BufferedReader inline and return the inner resource.
     * @param operation The operation to apply.
     * @return The inner resource.
     * @throws IOException if the passed in closure throws
     */
    public T with(BiConsumer<BufferedReaderOf<T>, T> operation) throws IOException {
        operation.accept(this, getInner());
        return getInner();
    }
} public class BufferedWriterOf<T> extends BufferedWriter implements WrapperOf<T> {

    final Resource<T> closer;

    /**
     * <p>
     * Constructor for BufferedWriterOf.
     * </p>
     *
     * @param cl a {@link ca.rbon.iostream.resource.Resource} object.
     * @param wr a {@link java.io.Writer} object.
     */
    public BufferedWriterOf(Resource<T> cl, Writer wr) {
        super(wr);
        closer = cl;
    }

    /** {@inheritDoc} */
    @Override
    public T getInner() throws IOException {
        return closer.getResource();
    }

    /**
     * Use the BufferedWriter inline and return the inner resource.
     * @param operation The operation to apply.
     * @return The inner resource.
     * @throws IOException if the passed in closure throws
     */
    public T with(BiConsumer<BufferedWriterOf<T>, T> operation) throws IOException {
        operation.accept(this, getInner());
        return getInner();
    }
} public class DataInputOf<T> extends DataInputStream implements WrapperOf<T> {

    final Resource<T> closer;

    /**
     * <p>
     * Constructor for DataInputOf.
     * </p>
     *
     * @param cl a {@link ca.rbon.iostream.resource.Resource} object.
     * @param is a {@link java.io.InputStream} object.
     */
    public DataInputOf(Resource<T> cl, InputStream is) {
        super(is);
        closer = cl;
    }

    /** {@inheritDoc} */
    @Override
    public T getInner() throws IOException {
        return closer.getResource();
    }

    /**
     * Use the DataInputStream inline and return the inner resource.
     * @param operation The operation to apply.
     * @return The inner resource.
     * @throws IOException if the passed in closure throws
     */
    public T with(BiConsumer<DataInputOf<T>, T> operation) throws IOException {
        operation.accept(this, getInner());
        return getInner();
    }
} public class DataOutputOf<T> extends DataOutputStream implements WrapperOf<T> {

    final Resource<T> closer;

    /**
     * <p>
     * Constructor for DataOutputOf.
     * </p>
     *
     * @param cl a {@link ca.rbon.iostream.resource.Resource} object.
     * @param os a {@link java.io.OutputStream} object.
     */
    public DataOutputOf(Resource<T> cl, OutputStream os) {
        super(os);
        closer = cl;
    }

    /** {@inheritDoc} */
    @Override
    public T getInner() throws IOException {
        return closer.getResource();
    }

    /**
     * Use the DataOutputStream inline and return the inner resource.
     * @param operation The operation to apply.
     * @return The inner resource.
     * @throws IOException if the passed in closure throws
     */
    public T with(BiConsumer<DataOutputOf<T>, T> operation) throws IOException {
        operation.accept(this, getInner());
        return getInner();
    }
} public class InputStreamOf<T> extends InputStream implements WrapperOf<T> {

    final InputStream delegate;

    final Resource<T> closer;

    /**
     * <p>
     * Constructor for InputStreamOf.
     * </p>
     *
     * @param cl a {@link ca.rbon.iostream.resource.Resource} object.
     * @param os a {@link java.io.InputStream} object.
     */
    public InputStreamOf(Resource<T> cl, InputStream os) {
        delegate = os;
        closer = cl;
    }

    /** {@inheritDoc} */
    @Override
    public T getInner() throws IOException {
        return closer.getResource();
    }

    /** {@inheritDoc} */
    @Override
    public int read() throws IOException {
        return delegate.read();
    }

    /** {@inheritDoc} */
    @Override
    public void close() throws IOException {
        delegate.close();
    }

    /**
     * Create an IntStream from this InputStream's bytes. An IOException may be
     * thrown while processing the IntStream. Consuming the IntStream entirely does
     * not close this InputStream.
     * 
     * @return an IntStream
     */
    public IntStream intStream() {
        return StreamInputAdapter.toIntStream(this);
    }

    /**
     * Use the InputStream inline and return the inner resource.
     * @param operation The operation to apply.
     * @return The inner resource.
     * @throws IOException if the passed in closure throws
     */
    public T with(BiConsumer<InputStreamOf<T>, T> operation) throws IOException {
        operation.accept(this, getInner());
        return getInner();
    }
} public class ObjectInputOf<T> extends ObjectInputStream implements WrapperOf<T> {

    final Resource<T> closer;

    /**
     * <p>
     * Constructor for ObjectInputOf.
     * </p>
     *
     * @param cl a {@link ca.rbon.iostream.resource.Resource} object.
     * @param is a {@link java.io.InputStream} object.
     * @throws java.io.IOException if any.
     */
    public ObjectInputOf(Resource<T> cl, InputStream is) throws IOException {
        super(is);
        closer = cl;
    }

    /** {@inheritDoc} */
    @Override
    public T getInner() throws IOException {
        return closer.getResource();
    }

    /**
     * Use the ObjectInputStream inline and return the inner resource.
     * @param operation The operation to apply.
     * @return The inner resource.
     * @throws IOException if the passed in closure throws
     */
    public T with(BiConsumer<ObjectInputOf<T>, T> operation) throws IOException {
        operation.accept(this, getInner());
        return getInner();
    }
} public class ObjectOutputOf<T> extends ObjectOutputStream implements WrapperOf<T> {

    final Resource<T> closer;

    /**
     * <p>
     * Constructor for ObjectOutputOf.
     * </p>
     *
     * @param cl a {@link ca.rbon.iostream.resource.Resource} object.
     * @param os a {@link java.io.OutputStream} object.
     * @throws java.io.IOException if any.
     */
    public ObjectOutputOf(Resource<T> cl, OutputStream os) throws IOException {
        super(os);
        closer = cl;
    }

    /** {@inheritDoc} */
    @Override
    public T getInner() throws IOException {
        return closer.getResource();
    }

    /**
     * Use the ObjectOutputStream inline and return the inner resource.
     * @param operation The operation to apply.
     * @return The inner resource.
     * @throws IOException if the passed in closure throws
     */
    public T with(BiConsumer<ObjectOutputOf<T>, T> operation) throws IOException {
        operation.accept(this, getInner());
        return getInner();
    }
} public class OutputStreamOf<T> extends OutputStream implements WrapperOf<T> {

    final OutputStream delegate;

    final Resource<T> closer;

    /**
     * <p>
     * Constructor for OutputStreamOf.
     * </p>
     *
     * @param cl a {@link ca.rbon.iostream.resource.Resource} object.
     * @param os a {@link java.io.OutputStream} object.
     */
    public OutputStreamOf(Resource<T> cl, OutputStream os) {
        delegate = os;
        closer = cl;
    }

    /** {@inheritDoc} */
    @Override
    public T getInner() throws IOException {
        return closer.getResource();
    }

    /** {@inheritDoc} */
    @Override
    public void write(int b) throws IOException {
        delegate.write(b);
    }

    @Override
    public void close() throws IOException {
        delegate.close();
    }

    /**
     * Use the OutputStream inline and return the inner resource.
     * @param operation The operation to apply.
     * @return The inner resource.
     * @throws IOException if the passed in closure throws
     */
    public T with(BiConsumer<OutputStreamOf<T>, T> operation) throws IOException {
        operation.accept(this, getInner());
        return getInner();
    }
} public class PrintWriterOf<T> extends PrintWriter implements WrapperOf<T> {

    final Resource<T> closer;

    /**
     * <p>
     * Constructor for PrintWriterOf.
     * </p>
     *
     * @param cl a {@link ca.rbon.iostream.resource.Resource} object.
     * @param w  a {@link java.io.Writer} object.
     */
    public PrintWriterOf(Resource<T> cl, Writer w) {
        super(w);
        closer = cl;
    }

    /**
     * <p>
     * Constructor for PrintWriterOf.
     * </p>
     *
     * @param cl        a {@link ca.rbon.iostream.resource.Resource} object.
     * @param w         a {@link java.io.Writer} object.
     * @param autoFlush a boolean.
     */
    public PrintWriterOf(Resource<T> cl, Writer w, boolean autoFlush) {
        super(w, autoFlush);
        closer = cl;
    }

    /** {@inheritDoc} */
    @Override
    public T getInner() throws IOException {
        return closer.getResource();
    }

    /**
     * Use the PrintWriter inline and return the inner resource.
     * @param operation The operation to apply.
     * @return The inner resource.
     * @throws IOException if the passed in closure throws
     */
    public T with(BiConsumer<PrintWriterOf<T>, T> operation) throws IOException {
        operation.accept(this, getInner());
        return getInner();
    }

} public class ReaderOf<T> extends Reader implements WrapperOf<T> {

    final Reader delegate;

    final Resource<T> closer;

    /**
     * <p>
     * Constructor for ReaderOf.
     * </p>
     *
     * @param cl a {@link ca.rbon.iostream.resource.Resource} object.
     * @param os a {@link java.io.Reader} object.
     */
    public ReaderOf(Resource<T> cl, Reader os) {
        delegate = os;
        closer = cl;
    }

    /** {@inheritDoc} */
    @Override
    public void close() throws IOException {
        delegate.close();
    }

    /** {@inheritDoc} */
    @Override
    public T getInner() throws IOException {
        return closer.getResource();
    }

    /** {@inheritDoc} */
    @Override
    public int read(char[] cbuf, int off, int len) throws IOException {
        return delegate.read(cbuf, off, len);
    }

    /**
     * Use the Reader inline and return the inner resource.
     * @param operation The operation to apply.
     * @return The inner resource.
     * @throws IOException if the passed in closure throws
     */
    public T with(BiConsumer<ReaderOf<T>, T> operation) throws IOException {
        operation.accept(this, getInner());
        return getInner();
    }

} public class WriterOf<T> extends Writer implements WrapperOf<T> {

    final Writer delegate;

    final Resource<T> closer;

    /**
     * <p>
     * Constructor for WriterOf.
     * </p>
     *
     * @param cl a {@link ca.rbon.iostream.resource.Resource} object.
     * @param os a {@link java.io.Writer} object.
     */
    public WriterOf(Resource<T> cl, Writer os) {
        delegate = os;
        closer = cl;
    }

    /** {@inheritDoc} */
    @Override
    public void close() throws IOException {
        delegate.close();
    }

    /** {@inheritDoc} */
    @Override
    public T getInner() throws IOException {
        return closer.getResource();
    }

    /** {@inheritDoc} */
    @Override
    public void write(char[] cbuf, int off, int len) throws IOException {
        delegate.write(cbuf, off, len);
    }

    /** {@inheritDoc} */
    @Override
    public void flush() throws IOException {
        delegate.flush();
    }

    /**
     * Use the Writer inline and return the inner resource.
     * @param operation The operation to apply.
     * @return The inner resource.
     * @throws IOException if the passed in closure throws
     */
    public T with(BiConsumer<WriterOf<T>, T> operation) throws IOException {
        operation.accept(this, getInner());
        return getInner();
    }
} public class ZipInputOf<T> extends ZipInputStream implements WrapperOf<T> {

    final Resource<T> closer;

    /**
     * <p>
     * Constructor for ZipInputOf.
     * </p>
     *
     * @param cl a {@link ca.rbon.iostream.resource.Resource} object.
     * @param is a {@link java.io.InputStream} object.
     */
    public ZipInputOf(Resource<T> cl, InputStream is) {
        super(is);
        closer = cl;
    }

    /**
     * <p>
     * Constructor for ZipInputOf.
     * </p>
     *
     * @param cl a {@link ca.rbon.iostream.resource.Resource} object.
     * @param is a {@link java.io.InputStream} object.
     * @param cs a {@link java.nio.charset.Charset} object.
     */
    public ZipInputOf(Resource<T> cl, InputStream is, Charset cs) {
        super(is, cs);
        closer = cl;
    }

    /** {@inheritDoc} */
    @Override
    public T getInner() throws IOException {
        return closer.getResource();
    }

    /**
     * Use the ZipInputStream inline and return the inner resource.
     * @param operation The operation to apply.
     * @return The inner resource.
     * @throws IOException if the passed in closure throws
     */
    public T with(BiConsumer<ZipInputOf<T>, T> operation) throws IOException {
        operation.accept(this, getInner());
        return getInner();
    }

} public class ZipOutputOf<T> extends ZipOutputStream implements WrapperOf<T> {

    final Resource<T> closer;

    /**
     * <p>
     * Constructor for ZipOutputOf.
     * </p>
     *
     * @param cl a {@link ca.rbon.iostream.resource.Resource} object.
     * @param os a {@link java.io.OutputStream} object.
     */
    public ZipOutputOf(Resource<T> cl, OutputStream os) {
        super(os);
        closer = cl;
    }

    /**
     * <p>
     * Constructor for ZipOutputOf.
     * </p>
     *
     * @param cl a {@link ca.rbon.iostream.resource.Resource} object.
     * @param os a {@link java.io.OutputStream} object.
     * @param cs a {@link java.nio.charset.Charset} object.
     */
    public ZipOutputOf(Resource<T> cl, OutputStream os, Charset cs) {
        super(os, cs);
        closer = cl;
    }

    /** {@inheritDoc} */
    @Override
    public T getInner() throws IOException {
        return closer.getResource();
    }

    public ZipOutputOf<T> addZipEntry(ZipEntry entry, InputStream data) throws IOException {
        this.putNextEntry(entry);
        IOUtils.copy(data, this);
        this.closeEntry();
        return this;
    }

    /**
     * Use the ZipOutputStream inline and return the inner resource.
     * @param operation The operation to apply.
     * @return The inner resource.
     * @throws IOException if the passed in closure throws
     */
    public T with(BiConsumer<ZipOutputOf<T>, T> operation) throws IOException {
        operation.accept(this, getInner());
        return getInner();
    }
}
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import javax.crypto.Cipher;
import ca.rbon.iostream.CodeFlowError;
import ca.rbon.iostream.channel.filter.Base64Filter;
import ca.rbon.iostream.channel.filter.CipherFilter;
import ca.rbon.iostream.channel.filter.FilterFactory;
import ca.rbon.iostream.channel.filter.GzipFilter;
import ca.rbon.iostream.channel.part.ByteIn;
import ca.rbon.iostream.channel.part.ByteOut;
import ca.rbon.iostream.channel.part.CharIn;
import ca.rbon.iostream.channel.part.CharOut;
import ca.rbon.iostream.channel.part.Filter;
import ca.rbon.iostream.wrap.BufferedInputOf;
import ca.rbon.iostream.wrap.BufferedOutputOf;
import ca.rbon.iostream.wrap.BufferedReaderOf;
import ca.rbon.iostream.wrap.BufferedWriterOf;
import ca.rbon.iostream.wrap.DataInputOf;
import ca.rbon.iostream.wrap.DataOutputOf;
import ca.rbon.iostream.wrap.InputStreamOf;
import ca.rbon.iostream.wrap.ObjectInputOf;
import ca.rbon.iostream.wrap.ObjectOutputOf;
import ca.rbon.iostream.wrap.OutputStreamOf;
import ca.rbon.iostream.wrap.PrintWriterOf;
import ca.rbon.iostream.wrap.ReaderOf;
import ca.rbon.iostream.wrap.WriterOf;
import ca.rbon.iostream.wrap.ZipInputOf;
import ca.rbon.iostream.wrap.ZipOutputOf;
        throw new CodeFlowError(STREAM_NOT_SUPPORTED);
    }

    /**
     * <p>
     * filteredOut.
     * </p>
     * 
     * @return a {@link java.io.OutputStream} object.
     * @throws java.io.IOException if any.
     */
    private OutputStream filteredOut() throws IOException {
        OutputStream filteredOutput = getOutputStream();
        for (FilterFactory f : filters) {
            filteredOutput = f.filterOutput(filteredOutput);
        }
        return filteredOutput;
    }

    /**
     * <p>
     * filteredIn.
     * </p>
     * 
     * @return a {@link java.io.InputStream} object.
     * @throws java.io.IOException if any.
     */
    private InputStream filteredIn() throws IOException {
        InputStream filteredInput = getInputStream();
        for (FilterFactory f : filters) {
            filteredInput = f.filterInput(filteredInput);
        }
        return filteredInput;
    }

    // SOURCE

    private InputStream wrappedBufferedInput(Charset charset, int bufferSize) throws IOException {
        return buffer(filteredIn(), bufferSize);
    }

    private Reader wrappedBufferedReader(Charset charset, int bufferSize) throws IOException {
        Reader encoded = wrappedEncodedReader(charset);
        return buffer(encoded, bufferSize);
    }

    private Reader wrappedEncodedReader(Charset charset) throws IOException {
        Reader natural = getReader();
        Reader encoded = natural != null
                ? natural
                : streamReader(filteredIn(), charset);
        return encoded;
    }

    // SINK

    private OutputStream wrappedBufferOut(Charset charset, int bufferSize) throws IOException {
        return buffer(filteredOut(), bufferSize);
    }

    private Writer wrappedWriter(Charset charset, int bufferSize) throws IOException {
        Writer encoded = wrappedEncodedWriter(charset);
        return buffer(encoded, bufferSize);
    }

    private Writer wrappedEncodedWriter(Charset charset) throws IOException {
        Writer natural = getWriter();
        Writer encoded = natural != null
                ? natural
                : streamWriter(filteredOut(), charset);
        return encoded;
    }

    // INPUT

    /**
     * <p>
     * zipInputStream.
     * </p>
     *
     * @param charset    a {@link java.nio.charset.Charset} object.
     * @param bufferSize a int.
     * @return a {@link ca.rbon.iostream.wrap.ZipInputOf} object.
     * @throws java.io.IOException if any.
     */

    @Override
    public ZipInputOf<T> zipInputStream(Charset charset, int bufferSize) throws IOException {
        return charset == null
                ? new ZipInputOf<>(this, wrappedBufferedInput(null, bufferSize))
                : new ZipInputOf<>(this, wrappedBufferedInput(null, bufferSize), charset);
    }

    /**
     * <p>
     * bufferedInputStream.
     * </p>
     *
     * @param bufferSize a int.
     * @return a {@link ca.rbon.iostream.wrap.BufferedInputOf} object.
     * @throws java.io.IOException if any.
     */

    @Override
    public BufferedInputOf<T> bufferedInputStream(int bufferSize) throws IOException {
        return bufferSize > DEFAULT_BUFFER_SIZE
                ? new BufferedInputOf<>(this, filteredIn(), bufferSize)
                : new BufferedInputOf<>(this, filteredIn());
    }

    /**
     * <p>
     * inputStream.
     * </p>
     *
     * @return a {@link ca.rbon.iostream.wrap.InputStreamOf} object.
     * @throws java.io.IOException if any.
     */

    @Override
    public InputStreamOf<T> inputStream() throws IOException {