blob: 126a5fa59378a9ac8257c7268a786cc481d48c06 [file] [log] [blame]
// Copyright 2016 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.bazel.e4b.command;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
/**
* A wrapper output stream to output part of the result to a given output and extracting the other
* part with a selector function. The other part is return as a list of string.
*/
public class SelectOutputStream extends OutputStream {
private OutputStream output;
private Function<String, String> selector;
private boolean closed = false;
private List<String> lines = new LinkedList<>();
private ByteArrayOutputStream stream = new ByteArrayOutputStream();
/**
* Create a SelectOutputStream. <code>output<code> is the output stream where non-selected lines
* will be printed. <code>selector<code> is a function that will be called on each line. If
* <code>selector</code> returns a non null value, then the resulting value will be stored in a
* lines buffer that can be consumed with the {@link #getLines()} method. If <code>selector</code>
* returns a null value, the corresponding line will be send to <code>output</code>.
*
* <p>
* Both <code>output</code> and <code>selector</code> can be null. If <code>output</code> is null,
* unselected lines will be discarded. If <code>selector</code> is null, all lines will be
* considered as unselected.
*/
public SelectOutputStream(OutputStream output, Function<String, String> selector) {
super();
this.output = output;
this.selector = selector;
}
@Override
public void write(int b) throws IOException {
Preconditions.checkState(!closed, "Attempted to write on a closed stream");
byte b0 = (byte) b;
if (b0 == '\n') {
select(true);
} else {
stream.write(b);
}
}
private void select(boolean appendNewLine) throws UnsupportedEncodingException, IOException {
String line = null;
if (selector != null) {
line = selector.apply(stream.toString(StandardCharsets.UTF_8.name()));
}
if (line != null) {
lines.add(line);
} else if (output != null) {
if (appendNewLine) {
stream.write('\n');
}
output.write(stream.toByteArray());
}
stream.reset();
}
@Override
public void close() throws IOException {
Preconditions.checkState(!closed);
super.close();
select(false);
closed = true;
}
/**
* Returns the list of selected lines.
*/
ImmutableList<String> getLines() {
return ImmutableList.copyOf(lines);
}
}