Contents
Introduction
The humble console app: I write them all the time creating one off tests to explore new coding ideas. I even string up quick, menu based, test applications for testing remote device communications and command sets, where the number of commands can grow during the product development cycle. The advantage of course, is that to grow the application I only have to add a menu item and an event handler in the code. That is far preferable to squeezing more fields onto an already crowded Windows Forms application. Sometimes you don't know what you have till it's gone.
I decided to tackle Java this year and get a feel for the language and tools. I usually take on a new (for me) language by translating an application I have written into the new language and stumbling through the thorny issues as I encounter them. I was surprised at how many of the things I took for granted in C# turned out to be rather hard to do in Java; case in point, the console app.
The application I chose to port to Java happened to be a console based menu driven application. Porting the menu code was fairly straightforward once I figured out how to substitute an interface and nested Java class for a C# delegate in order to obtain callback behavior. The real headache lay in the performance of the application's menu code in an actual Windows console instance.
Java applications utilize STDIN
, STDOUT
, and STDERR
streams but do not own the console instance displaying the streams. Since Java aims at a broad range of Operating Systems, it doesn't include support for clearing the screen. I tried a number of work-a-rounds to get my application to function as intended without acceptable results.
I was about to give up when I took a different track and considered duplicating console functionality in a Java GUI widget. This turned out to be more promising. There were quite a few examples of redirecting STDOUT
and STDERR
to a JtextArea
but I was unable to find one that also successfully copied keyboard input to STDIN
. What I did find was quite a bit of chatter from other developers indicating a desire for such a component.
I began to test the various code solutions and snippets that I had found and settled on one particularly robust example[^]. I then added some code to pipe keyboard characters to STDIN
, added a public clear()
method, and some properties to customize the look and feel of the console. Finally, I figured out how to create a custom blinking block cursor for JavaConsole
to round things off.
How to Use this Java Console
Minimally, all that is necessary to use this Java Console in your project is to declare an instance of the JavaConsole
class.
public static void main(String[] args) {
new JavaConsole();
scanner = new Scanner(System.in);
System.out.println("Hello World!");
scanner.nextLine();
System.exit(0);
}
Here is an example of how to customize the look of the Java Console.
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
JavaConsole console = new JavaConsole();
console.setBackground(Color.WHITE);
console.setForeground(Color.BLACK);
console.setFont(new Font ("Ariel", Font.BOLD, 12));
console.setTitle("Hello World Program");
Image im = Toolkit.getDefaultToolkit().getImage("../images/MyIcon.png");
console.setIconImage(im);
System.out.println("Hello World!");
scanner.nextLine();
System.exit(0);
}
Here is an example using the JavaConsole.clear()
method to clear the screen between chunks of text.
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
JavaConsole console = new JavaConsole();
System.out.println(
"It was a dark and stormy night; the rain fell in torrents, except at " +
"occasional intervals, when it was checked by a violent gust of wind " +
"which swept up the streets (for it is in London that our scene lies), " +
"rattling along the house-tops, and fiercely agitating the scanty flame " +
"of the lamps that struggled against the darkness. Through one of the " +
"obscurest quarters of London, and among haunts little loved by the " +
"gentlemen of the police, a man, evidently of the lowest orders, was " +
"wending his solitary way.");
scanner.nextLine();
console.clear();
System.out.println(
"He stopped twice or thrice at different shops and houses of a description " +
"correspondent with the appearance of the quartier in which they were situated, " +
"and tended inquiry for some article or another which did not seem easily " +
"to be met with. All the answers he received were couched in the negative; " +
"and as he turned from each door he muttered to himself, in no very elegant " +
"phraseology, his disappointment and discontent. At length, at one house, " +
"the landlord, a sturdy butcher, after rendering the same reply the inquirer " +
"had hitherto received, added, \"But if this vill do as vell, Dummie, it is " +
"quite at your sarvice!\"");
scanner.nextLine();
System.exit(0);
}
Finally, here's a comprehensive list of Java Console custom features.
console.getBackground();
console.getForeground();
console.getFont();
console.setBackground(Color);
console.setForeground(Color);
console.setFont(Font);
console.setTitle(title);
console.setIconImage(Image);
console.clear();
How to Wire Up a Console
The first thing we need to do is to redirect STDOUT
and STDERR
to our JtextArea
. The code class I started with achieved this in the following manner:
private final PipedInputStream pin=new PipedInputStream();
private final PipedInputStream pin2=new PipedInputStream();
PipedOutputStream pout=new PipedOutputStream(this.pin);
PipedOutputStream pout2=new PipedOutputStream(this.pin2);
System.setOut(new PrintStream(pout,true));
System.setErr(new PrintStream(pout2,true));
private Thread reader;
private Thread reader2;
reader=new Thread(this);
reader.setDaemon(true);
reader.start();
reader2=new Thread(this);
reader2.setDaemon(true);
reader2.start();
public synchronized void run()
{
try
{
while (Thread.currentThread()==reader)
{
try { this.wait(100);}catch(InterruptedException ie) {}
if (pin.available()!=0)
{
String input=this.readLine(pin);
textArea.append(input);
textArea.setCaretPosition(textArea.getDocument().getLength());
}
if (quit) return;
}
while (Thread.currentThread()==reader2)
{
try { this.wait(100);}catch(InterruptedException ie) {}
if (pin2.available()!=0)
{
String input=this.readLine(pin2);
textArea.append(input);
textArea.setCaretPosition(textArea.getDocument().getLength());
}
if (quit) return;
}
} catch (Exception e)
{
textArea.append("\nConsole reports an Internal error.");
textArea.append("The error is: "+e);
textArea.setCaretPosition(textArea.getDocument().getLength());
}
}
- Establish some Piped Input Streams.
- Hook the input streams up to the associated output streams.
- Redirect System
STDOUT
and STDERR
to these output streams. - Spin up some threads to read from the Piped Input Streams.
- Monitor
STDOUT
and STDERR
appending the text to the JtextArea
.
Now that we have STDOUT
and STDERR
piped to the JtextArea
, we need to monitor keyboard input to the JtextArea
and copy the characters to STDIN
. I modified the original code to do so as follows:
private final PipedOutputStream pout3=new PipedOutputStream();
System.setIn(new PipedInputStream(this.pout3));
textArea.addKeyListener(new KeyListener() {
public void keyPressed(KeyEvent e) {}
public void keyReleased(KeyEvent e) {}
public void keyTyped(KeyEvent e) {
try { pout3.write(e.getKeyChar()); } catch (IOException ex) {}
}});
- Establish a Piped Output Stream for reading keyboard input.
- Hook the output stream to an associated input stream and redirect
STDIN
to this input stream. - Add a
KeyListener
and copy out keyboard input as it arrives.
Making a Custom Block Caret
The default caret of the JtextArea
is a vertical bar but I wanted a prompt that was a bit more prominent. It turns out that this feature is customizable and I found some examples on how to do this. Refer to A custom caret class [^] and Fancier custom caret class [^] to get an idea of how it's done. Here is the code for my flashing block caret:
public class BlockCaret extends DefaultCaret {
private static final long serialVersionUID = 1L;
public BlockCaret() {
setBlinkRate(500);
}
protected synchronized void damage(Rectangle r) {
if (r == null)
return;
x = r.x;
y = r.y;
height = r.height;
if (width <= 0)
width = getComponent().getWidth();
repaint();
repaint();
}
public void paint(Graphics g) {
JTextComponent comp = getComponent();
if (comp == null)
return;
int dot = getDot();
Rectangle r = null;
char dotChar;
try {
r = comp.modelToView(dot);
if (r == null)
return;
dotChar = comp.getText(dot, 1).charAt(0);
} catch (BadLocationException e) {
return;
}
if(Character.isWhitespace(dotChar)) dotChar = '_';
if ((x != r.x) || (y != r.y)) {
damage(r);
return;
}
g.setColor(comp.getCaretColor());
g.setXORMode(comp.getBackground());
width = g.getFontMetrics().charWidth(dotChar);
if (isVisible())
g.fillRect(r.x, r.y, width, r.height);
}
}
Extra! Extra! The Menu System
I have included the menu system, that I like to use so much, in this demo. It is a Java port of the easy to use console menu featured in this CodeProject article [^]. As I mentioned in the Introduction, working with callbacks in Java is a bit different than C# and the Menu
class illustrates this well. Here's how it's done in C#.
delegate void MenuCallback();
private static void HandleItem1
{
Console.WriteLine("You chose item 1.\n");
Console.Read();
}
private static void HandleItem2
{
Console.WriteLine("You chose item 2.\n");
Console.Read();
}
menu = new Menu();
menu.Add("Item 1", new MenuCallback(HandleItem1));
menu.Add("Item 2", new MenuCallback(HandleItem2));
menu.Show();
- I declare a delegate (a function pointer)
- Next, I create some event handlers for menu picks
- Then, I add them to the
Menu
like so
Java does not allow for the passing of function pointers and thus delegates. Instead, this behavior is restricted to typed object pointers and thus the class interface becomes the Java equivalent of the delegate. Here's how it's done in Java.
public interface MenuCallback extends EventListener {
public void Invoke();
}
private static void HandleItem1 {
System.out.println("You chose item 1.\n.");
scanner.nextLine();
}
private static void HandleItem2 {
System.out.println("You chose item 2.\n.");
scanner.nextLine();
}
Menu menu = new Menu(console);
menu.add("Item 1", new MenuCallback() { public void Invoke() {
HandleItem1(); } });
menu.add("Item 2", new MenuCallback() { public void Invoke() {
HandleItem2(); } });
menu.show();
- I declare an interface (a typed object pointer)
- Next, I create some event handlers for menu picks
- Then I add them to the
Menu
like so
It's a bit more verbose since we are in effect, having to wrap our function pointer in a class.
Final Comments
The menu system does not work well in the standard console when used in a Java application. The version of Menu
class included here should only be used in this Java Console. This Java Console may be used anytime you want to have a console window that is owned by your application. While it features a clear()
method for clearing the screen, it does not, however handle DOS commands or provide a command prompt.
History
- Feb 09, 2012: Version 1.0.0.0