Como escrever em vários PrintStreams simultaneamente

2023/11/01

O padrão de projeto Composite permite que vários objetos sejam manipulados como se fossem um único objeto.

A implementação da classe PrintStreams foi baseada no AWTEventMulticaster.

Ela permite propagar chamadas de métodos para vários objetos PrintStream como se fossem um só, eliminando a necessidade de escrever loops para iterar os objetos.

Exemplo de uso:

PrintStream arquivo1 = new PrintStream(new File("arquivo1.log"));
PrintStream arquivo2 = new PrintStream(new File("arquivo2.log"));

PrintStream saida = System.out;
            saida = PrintStreams.attach(saida, arquivo1);
            saida = PrintStreams.attach(saida, arquivo2);

saida.println("Esse conteúdo será escrito tanto no console quanto no arquivo1.log e arquivo2.log");

Fonte da classe PrintStreams:

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.Locale;

/**
 * Classe utilitária que permite encadear {@link PrintStream}s de forma que sejam utilizados como um único objeto.<br>
 * É uma forma de implementação do padrão de projeto <I>Composite</I>.
 */
public final class PrintStreams extends PrintStream {

    // Um OutputStream que serve apenas de parâmetro para o construtor da superclasse
    private static final OutputStream EMPTY_OUTPUT = new OutputStream() { @Override public void write(int b) throws IOException {} };

    /**
     * Conecta um {@link PrintStream} ao {@link PrintStream} existente. 
     * 
     * @param existingPrintStream O {@link PrintStream} existente, pode ser <tt>null</tt>.
     * @param printStreamToAttach O {@link PrintStream} a ser conectado, caso seja <tt>null</tt> não terá efeito algum.
     * 
     * @return Um {@link PrintStream} composto pelo {@link PrintStream} original mais o {@link PrintStream} adicionado.
     */
    public static PrintStream attach(PrintStream existingPrintStream, PrintStream printStreamToAttach) {
        return attachInternal(existingPrintStream, printStreamToAttach);
    }

    
    /**
     * Desonecta um {@link PrintStream} do {@link PrintStream} existente. 
     * 
     * @param existingPrintStream O {@link PrintStream} existente.
     * @param printStreamToDetach O {@link PrintStream} a ser desconectado.
     * 
     * @return Um {@link PrintStream} que não possui mais o {@link PrintStream} desconectado em sua composição.
     */
    public static PrintStream detach(PrintStream existingPrintStream, PrintStream printStreamToDetach) {
        return detachInternal(existingPrintStream, printStreamToDetach);
    }

    private static PrintStream attachInternal(PrintStream existingPrintStream, PrintStream printStreamToAttach) {
        if (existingPrintStream == null) {
            return printStreamToAttach;
        }
        if (printStreamToAttach == null) {
            return existingPrintStream;
        }
        return new PrintStreams(existingPrintStream, printStreamToAttach);
    }

    private static PrintStream detachInternal(PrintStream existingPrintStream, PrintStream printStreamToDetach) {
        if (existingPrintStream == printStreamToDetach || existingPrintStream == null) {
            return null;
        }
        if (existingPrintStream instanceof PrintStreams tuple) {
            if (printStreamToDetach == tuple.a) {
                return tuple.b;
            }
            if (printStreamToDetach == tuple.b) {
                return tuple.a;
            }
            PrintStream a = detachInternal(tuple.a, printStreamToDetach);
            PrintStream b = detachInternal(tuple.b, printStreamToDetach);
            if (a == tuple.a && b == tuple.b) {
                return tuple;
            }
            return attachInternal(a, b);
        }
        return existingPrintStream;
    }

    private final PrintStream a;
    private final PrintStream b;

    private PrintStreams(PrintStream a, PrintStream b) {
        super(EMPTY_OUTPUT);
        this.a = a;
        this.b = b;
    }

    @Override
    public PrintStream append(char value) {
        a.append(value);
        b.append(value);
        return this;
    }

    @Override
    public PrintStream append(CharSequence value) {
        a.append(value);
        b.append(value);
        return this;
    }

    @Override
    public PrintStream append(CharSequence value, int start, int end) {
        a.append(value, start, end);
        b.append(value, start, end);
        return this;
    }

    @Override
    public boolean checkError() {
        boolean error = false;
        error |= a.checkError();
        error |= b.checkError();
        return error;
    }

    @Override
    public void close() {
        a.close();
        b.close();
    }

    @Override
    public void flush() {
        a.flush();
        b.flush();
    }

    @Override
    public PrintStream format(Locale locale, String format, Object... values) {
        a.format(locale, format, values);
        b.format(locale, format, values);
        return this;
    }

    @Override
    public PrintStream format(String format, Object... values) {
        a.format(format, values);
        b.format(format, values);
        return this;
    }

    @Override
    public void print(boolean value) {
        a.print(value);
        b.print(value);
    }

    @Override
    public void print(char value) {
        a.print(value);
        b.print(value);
    }

    @Override
    public void print(char[] value) {
        a.print(value);
        b.print(value);
    }

    @Override
    public void print(double value) {
        a.print(value);
        b.print(value);
    }

    @Override
    public void print(float value) {
        a.print(value);
        b.print(value);
    }

    @Override
    public void print(int value) {
        a.print(value);
        b.print(value);
    }

    @Override
    public void print(long value) {
        a.print(value);
        b.print(value);
    }

    @Override
    public void print(Object value) {
        a.print(value);
        b.print(value);
    }

    @Override
    public void print(String value) {
        a.print(value);
        b.print(value);
    }

    @Override
    public PrintStream printf(Locale locale, String format, Object... values) {
        a.printf(locale, format, values);
        b.printf(locale, format, values);
        return this;
    }

    @Override
    public PrintStream printf(String format, Object... values) {
        a.printf(format, values);
        b.printf(format, values);
        return this;
    }

    @Override
    public void println() {
        a.println();
        b.println();
    }

    @Override
    public void println(boolean value) {
        a.println(value);
        b.println(value);
    }

    @Override
    public void println(char value) {
        a.println(value);
        b.println(value);
    }

    @Override
    public void println(char[] value) {
        a.println(value);
        b.println(value);
    }

    @Override
    public void println(double value) {
        a.println(value);
        b.println(value);
    }

    @Override
    public void println(float value) {
        a.println(value);
        b.println(value);
    }

    @Override
    public void println(int value) {
        a.println(value);
        b.println(value);
    }

    @Override
    public void println(long value) {
        a.println(value);
        b.println(value);
    }

    @Override
    public void println(Object value) {
        a.println(value);
        b.println(value);
    }

    @Override
    public void println(String value) {
        a.println(value);
        b.println(value);
    }

    @Override
    public void write(byte[] buffer) throws IOException {
        write(buffer, 0, buffer.length);
    }

    @Override
    public void write(byte[] buffer, int offset, int length) {
        a.write(buffer, offset, length);
        b.write(buffer, offset, length);
    }

    @Override
    public void write(int bits) {
        a.write(bits);
        b.write(bits);
    }

    public void writeBytes(byte[] buffer) {
        write(buffer, 0, buffer.length);
    }

    @Override
    public String toString() {
        return a + " -> " + b;
    }
}