/* $Id: shell.java,v 1.2 2002/08/01 04:17:15 comoc Exp $ */
/*
* `gnu.iou' I/O buffers and utilities.
* Copyright (C) 1998, 1999, 2000, 2001, 2002 John Pritchard.
*
* This program is free software; you can redistribute it or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package gnu.iou.sh;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.PrintStream;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.border.Border;
/**
* An extensible interpreter pane requires a plugin for an interpreter.
*
* @author John Pritchard (john@syntelos.org)
*/
public class Shell extends JPanel implements Runnable {
//
/**
* The shell will call on an interpretor with this interface.
*
* @author John Pritchard
*/
public static interface Plugin {
/**
* String displayed in the UI, under the console, with the interpreter's
* name and version.
*/
public String userVersion();
/**
* The interpreter handles I/O on this interface as if on the command
* line, until this method throws an exception.
*
* @param stdin
* GUI console input.
*
* @param stdout
* GUI console output.
*
* @param stderr
* GUI console output.
*/
public void console(DataInputStream stdin, PrintStream stdout, PrintStream stderr) throws IOException;
/**
* Called with an exception from the "console" method. If the "console"
* method returns, interpretation continues. However, if it throws an
* exception, this method is called with that exception to verify that
* the shell interpretation thread should exit.
*
* @param exc
* Exception thrown by the console.
*
* @param stdout
* GUI console output.
*
* @param stderr
* GUI console output.
*
* @return Whether to continue the shell intepretation thread or return.
*/
public boolean exception(Exception exc, PrintStream stdout, PrintStream stderr);
}
//
/**
* Instantiate plugin dynamically from classname. The class has a public,
* simple (no args) constructor.
*
* @param classname
* Fully qualified name of a class implementing the <code>`shell.plugin'</code>
* interface.
*/
protected final static Plugin create(String classname) {
try {
Class cla = Class.forName(classname);
return (Plugin) cla.newInstance();
} catch (ClassNotFoundException cnf) {
throw new IllegalArgumentException("Plugin class not found (" + classname + ").");
} catch (ClassCastException ccx) {
throw new IllegalArgumentException("Class not a `shell.plugin' (" + classname + ").");
} catch (InstantiationException insx) {
throw new IllegalArgumentException("Plugin class is abstract (" + classname + ").");
} catch (IllegalAccessException ilac) {
throw new IllegalArgumentException("Plugin constructor is not public (" + classname + ").");
}
}
//
private Plugin interp;
private DataInputStream stdin;
private PrintStream stdout;
private PrintStream stderr;
private Thread actor = null;
protected JLabel status = new JLabel();
private final ShellTextArea window;
/**
* Construct a new GUI for the interpreter plugin, no border.
*
* @param classname
* Interpreter plugin class name.
*/
public Shell(String classname) {
this(null, create(classname), 0, 0);
}
/**
* Construct a new GUI for the interpreter plugin, no border.
*
* @param interp
* Interpreter plugin.
*/
public Shell(Plugin interp) {
this(null, interp, 0, 0);
}
/**
* Construct a new GUI for the interpreter plugin with a border.
*
* @param interp
* Interpreter plugin.
*
* @param border
* Set this border on this pane.
*/
public Shell(Border border, Plugin interp) {
this(border, interp, 0, 0);
}
/**
* Construct a new GUI for the interpreter plugin with fixed dimensions, no
* border.
*
* @param interp
* Interpreter plugin.
*
* @param rows
* Number of rows in text area.
*
* @param columns
* Number of columns in text area.
*/
public Shell(Plugin interp, int rows, int columns) {
this(null, interp, rows, columns);
}
/**
* Construct a new GUI for the interpreter plugin with fixed dimensions and
* an optional border.
*
* @param border
* Optional border around this panel.
*
* @param interp
* Interpreter plugin.
*
* @param rows
* Number of rows in text area.
*
* @param columns
* Number of columns in text area.
*/
public Shell(Border border, Plugin interp, int rows, int columns) {
super();
if (null == interp)
throw new IllegalArgumentException("Require interpreter.");
else
this.interp = interp;
if (null != border)
setBorder(border);
this.window = new ShellTextArea(rows, columns);
this.stdin = this.window.getStdin();
this.stdout = this.window.getStdout();
this.stderr = this.window.getStderr();
setLayout(new BorderLayout());
JScrollPane scroller = new JScrollPane(this.window);
add(scroller, BorderLayout.CENTER);
this.status.setFont(new Font("SansSerif", Font.PLAIN, 10));
add(this.status, BorderLayout.SOUTH);
}
/**
* The interpreter's version string for the UI.
*/
public String userVersion() {
return this.interp.userVersion();
}
/**
* Send the argument statement to be evaluated in the interpreter.
*/
public void println(String stmt) throws IOException {
if (null != stmt)
this.window.println(stmt);
}
/**
* Run the interpreter.
*/
public void run() {
status_reset();
while (true) {
try {
this.interp.console(stdin, stdout, stderr);
status_reset();
} catch (Exception exc) {
if (this.interp.exception(exc, stdout, stderr))
continue;
else
return;
}
}
}
/**
* Get status label text.
*/
public String status() {
return this.status.getText();
}
/**
* Set status label text.
*/
public void status(String msg) {
this.status.setText(msg);
}
/**
* Set status label to interp user version string.
*/
public void status_reset() {
this.status.setText(this.interp.userVersion());
}
/**
* Create and start thread for this shell.
*/
public void start() {
if (null == this.actor) {
this.actor = new Thread(this);
this.actor.start();
}
}
public void stop() {
if (null != this.actor) {
this.actor.stop();
this.actor = null;
}
}
public void suspend() {
if (null != this.actor)
this.actor.suspend();
}
public void resume() {
if (null != this.actor)
this.actor.resume();
}
//
/**
* Command line function creates a frame UI. Requires "plugin" classname
* argument.
*/
public final static void main(String[] argv) {
try {
if (null == argv || 1 != argv.length)
throw new IllegalArgumentException();
Shell sh = new Shell(argv[0]);
sh.setPreferredSize(new Dimension(500, 400));
JFrame frame = new JFrame(sh.userVersion());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(sh);
frame.pack();
frame.show();
} catch (IllegalArgumentException ilarg) {
String msg = ilarg.getMessage();
if (null == msg) {
System.err.println();
System.err.println(" Usage: shell classname");
System.err.println();
System.err.println(" Instantiate the named class as a ");
System.err.println(" shell plugin interpreter.");
System.err.println();
} else
System.err.println("Error: " + msg);
System.exit(1);
}
}
}