001// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
002//
003// SPDX-License-Identifier: Apache-2.0
004
005package sop.util;
006
007import java.io.ByteArrayOutputStream;
008import java.io.IOException;
009import java.io.OutputStream;
010
011/**
012 * {@link OutputStream} that buffers data being written into it, until its underlying output stream is being replaced.
013 * At that point, first all the buffered data is being written to the underlying stream, followed by any successive
014 * data that may get written to the {@link ProxyOutputStream}.
015 *
016 * This class is useful if we need to provide an {@link OutputStream} at one point in time when the final
017 * target output stream is not yet known.
018 */
019public class ProxyOutputStream extends OutputStream {
020
021    private final ByteArrayOutputStream buffer;
022    private OutputStream swapped;
023
024    public ProxyOutputStream() {
025        this.buffer = new ByteArrayOutputStream();
026    }
027
028    public synchronized void replaceOutputStream(OutputStream underlying) throws IOException {
029        if (underlying == null) {
030            throw new NullPointerException("Underlying OutputStream cannot be null.");
031        }
032        this.swapped = underlying;
033
034        byte[] bufferBytes = buffer.toByteArray();
035        swapped.write(bufferBytes);
036    }
037
038    @Override
039    public synchronized void write(byte[] b) throws IOException {
040        if (swapped == null) {
041            buffer.write(b);
042        } else {
043            swapped.write(b);
044        }
045    }
046
047    @Override
048    public synchronized void write(byte[] b, int off, int len) throws IOException {
049        if (swapped == null) {
050            buffer.write(b, off, len);
051        } else {
052            swapped.write(b, off, len);
053        }
054    }
055
056    @Override
057    public synchronized void flush() throws IOException {
058        buffer.flush();
059        if (swapped != null) {
060            swapped.flush();
061        }
062    }
063
064    @Override
065    public synchronized void close() throws IOException {
066        buffer.close();
067        if (swapped != null) {
068            swapped.close();
069        }
070    }
071
072    @Override
073    public synchronized void write(int i) throws IOException {
074        if (swapped == null) {
075            buffer.write(i);
076        } else {
077            swapped.write(i);
078        }
079    }
080}