I wrote a Java CLI program that reads lines from stdin and outputs a possible completion for each line. I’m attempting to give it a gui, so I’m trying to build a drop-in replacement for System.in, to allow users to either use the gui or the cli.
So far, I got this replacement, whose method add is called when text is inputted in a JTextArea:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.InterruptedIOException;
import java.util.LinkedList;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
public class GuiIn extends InputStream {
protected LinkedBlockingQueue<Byte> buf;
protected boolean closed;
public GuiIn() {
closed = false;
buf = new LinkedBlockingQueue<Byte>();
}
@Override
public void close() {
closed = true;
}
/**
* add strings to read in the InputStream. Arguments are ignored if the
* stream is closed.
*
* @param s
* a string. Ignored if null
*/
public void add(String s) {
if (closed || s == null) {
return;
}
byte[] bs = s.getBytes();
LinkedList<Byte> lbs = new LinkedList<Byte>();
for (byte b : bs) {
lbs.add(b);
}
buf.addAll(lbs);
}
@Override
public int available() {
return buf.size();
}
@Override
public synchronized int read() throws InterruptedIOException {
if (closed && buf.isEmpty()) {
return -1;
}
Byte b = 0;
while (true) {
try {
if ((b = buf.poll(100, TimeUnit.MILLISECONDS)) == null) {
if (closed && buf.isEmpty())
return -1;
} else
break;
} catch (InterruptedException e) {
throw new InterruptedIOException("interrupted: "
+ e.getMessage());
}
}
return b;
}
}
However, when I tried it out with new BufferedReader(new InputStreamReader(in)); and attempted to readLine() it, it seems to block until it has enough characters (many), despite the fact that the text fed is always appended with a newline by my listener.
On the other hand, if in is set to System.in, each line is read as soon as it is inputted.
So, my question comes in two parts:
- Where does this difference come from?
- How to fix it?
Please note that reading from the bare GuiIn byte per byte works correctly, and that I already tried tricks like reducing the size of BufferedReader‘s buffer.
I also searched the web beforehand: this is not about creating a mock object; and ByteArrayInputStream is not an option either: it does not support appending.
One source of your problem might be that
InputStreamReadermight insist on reading farther ahead so that it can be sure thebyte->chardecoder it uses (and which is opaque to it) has enough bytes to produce a fullcharwhileSystem.inmight know enough about the default encoding to provide just enough bytes.Before you start doing interactive console stuff with Java you should become familiar with the
Consoleclass and the readline to Java port.Then, instead of creating
InputStreamReaders without specifying the encoding, I would abstract at a higher level:and then you can create one backed by the
FileDescriptor.STDIN, and another by whatever you like so that you can automate input for testing.