package xapi.io.api;
import xapi.collect.api.Fifo;
import xapi.collect.impl.SimpleFifo;
import xapi.log.X_Log;
import xapi.util.X_String;
/**
* An implementation of {@link LineReader} designed to stream lines of text to one or more
* delegate LineReaders. It also stores all input so that a new LineReader can be added
* at any time, and it will still receive all text that was streamed to this reader.
* <p>
* This is handy for such use cases as forwarding an external processes std in and std out,
* where we might need to add a listener to the process after it has already started.
* <p>
* This class has also been enhanced with the {@link #waitToEnd()} method,
* which will block until the line producer (such as a thread streaming input from an external process)
* has signalled that the stream is finished by calling {@link #onEnd()}.
*
* @author "James X. Nelson (james@wetheinter.net)"
*
*/
public class StringReader implements LineReader {
private StringBuilder b;
private final Fifo<LineReader> delegates = new SimpleFifo<LineReader>();
boolean finished;
@Override
public void onStart() {
b = new StringBuilder();
if (delegates.isEmpty())return;
for (LineReader delegate : delegates.forEach()) {
delegate.onStart();
}
}
@Override
public void onLine(String line) {
synchronized (this) {
b.append(line);
if (delegates.isEmpty())return;
for (LineReader delegate : delegates.forEach()) {
delegate.onLine(line);
}
}
}
@Override
public final void onEnd() {
X_Log.trace(getClass(),"ending", this);
try {
for (LineReader delegate : delegates.forEach()) {
X_Log.debug(getClass(),"ending delegate", delegate.getClass(), delegate);
delegate.onEnd();
}
} finally {
finished = true;
onEnd0();
}
synchronized (delegates) {
delegates.notifyAll();
}
}
protected void onEnd0() {
}
@Override
public String toString() {
return String.valueOf(b);
}
public synchronized void forwardTo(LineReader callback) {
X_Log.debug(getClass(),getClass().getName(),"forwardingTo", callback.getClass().getName(),":", callback);
if (b != null) {// not null only after we have started streaming
callback.onStart();
for (String line : X_String.splitNewLine(b.toString())) {
callback.onLine(line);
}
}
delegates.give(callback);
if (finished) {
callback.onEnd();
}
}
public void waitToEnd() throws InterruptedException {
if (finished) {
return;
}
synchronized (delegates) {
delegates.wait();
}
}
public void waitToEnd(long timeout, int nanos) throws InterruptedException {
if (finished) {
return;
}
synchronized (delegates) {
delegates.wait(timeout, nanos);
}
}
}