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;
}
}