// Copyright 2014 The Bazel Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.devtools.build.lib.util.io; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.List; /** * An {@link OutErr} specialization that supports subscribing / removing * sinks, using {@link #addSink(OutErr)} and {@link #removeSink(OutErr)}. * A sink is a destination to which the {@link DelegatingOutErr} will write. * * Also, we can hook up {@link System#out} / {@link System#err} as sources. */ public final class DelegatingOutErr extends OutErr { /** * Create a new instance to which no sinks have subscribed (basically just * like a {@code /dev/null}. */ public DelegatingOutErr() { super(new DelegatingOutputStream(), new DelegatingOutputStream()); } private final DelegatingOutputStream outSink() { return (DelegatingOutputStream) getOutputStream(); } private final DelegatingOutputStream errSink() { return (DelegatingOutputStream) getErrorStream(); } /** * Add a sink, that is, after calling this method, {@code outErrSink} will * receive all output / errors written to {@code this} object. */ public void addSink(OutErr outErrSink) { outSink().addSink(outErrSink.getOutputStream()); errSink().addSink(outErrSink.getErrorStream()); } /** * Remove the sink, that is, after calling this method, {@code outErrSink} * will no longer receive output / errors written to {@code this} object. */ public void removeSink(OutErr outErrSink) { outSink().removeSink(outErrSink.getOutputStream()); errSink().removeSink(outErrSink.getErrorStream()); } private static class DelegatingOutputStream extends OutputStream { private final List<OutputStream> sinks = new ArrayList<>(); public void addSink(OutputStream sink) { sinks.add(sink); } public void removeSink(OutputStream sink) { sinks.remove(sink); } @Override public void write(int b) throws IOException { for (OutputStream sink : sinks) { sink.write(b); } } @Override public void close() throws IOException { for (OutputStream sink : sinks) { sink.close(); } } @Override public void flush() throws IOException { for (OutputStream sink : sinks) { sink.flush(); } } @Override public void write(byte[] b, int off, int len) throws IOException { for (OutputStream sink : sinks) { sink.write(b, off, len); } } @Override public void write(byte[] b) throws IOException { for (OutputStream sink : sinks) { sink.write(b); } } } }