view src/samer/core_/Shell.java @ 1:5df24c91468d

Oh my what a mess.
author samer
date Fri, 05 Apr 2019 16:26:00 +0100
parents bf79fb79ee13
children
line wrap: on
line source
/*
 *	Shell.java	
 *
 *	Copyright (c) 2000, Samer Abdallah, King's College London.
 *	All rights reserved.
 *
 *	This software is provided AS IS and WITHOUT ANY WARRANTY; 
 *	without even the implied warranty of MERCHANTABILITY or
 *	FITNESS FOR A PARTICULAR PURPOSE.
 */

package samer.core;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;

import samer.core.Environment.Datum;
import samer.core.Environment.Binding;
import samer.core.Environment.Autocoder;



/**
	This class provides a set of static methods for
	some useful services. The actual implementation is 
	provided by some object that implements the
	Shell.Interface interface.
  */

public class Shell
{
	/** All shells must implement this interface. It provides service that
	  *   are generally useful but can be implemented in different ways, such
	  *   as printing messages and getting usable windows. */

	public interface Interface
	{	
		// different types of message
		void print(String msg); 
		void status(String msg); 
		void trace(String msg); 

		// factory methods
		PrintWriter	getPrintWriter();
		Window		getWindow(String title);
		Dialog			getDialog(String title); 

		Container		createButtonsFor(Agent agent);
		Viewer		createViewerPanel(Viewer vwr);
		Component	createLabel(String nm);

		NumberViewer createNumberViewer( String label, int flags, NumberSink s);

		// agents and viewables
		void registerViewable(Viewable v);
		void deregisterViewable(Viewable v);
		void registerAgent(Agent a);
		void deregisterAgent(Agent a);
		void exposeCommands(Agent agent);
		void exit();
	}


	/**
		Interface returned by the getWindow method of the
		Shell.Interface interface. The window is not necessarily
		visible on return -- use the <code>expose()</code>
		method to make it so.
		NB: distinct from java.awt.Window.
	 */

	public interface Window {
		/** Make window visible */
		void expose();

		/** Hide and destroy window */
		void dispose();

		/** Should behave just like in <code>java.awt.Window</code> */
		void addWindowListener(WindowListener wl);

		/** Returns the AWT container for this window, in to
			which components can be added in the normal way */
		Container container();
	}

	/**
		Interface returned by the getDialog method of the
		Shell.Interface interface. The dialog is not necessarily
		visible on return -- use the <code>expose()</code>
		method to make it so.
		<p>A dialog is distinct from a window in that it
		is modal, and is dismissed by one of a set of buttons.
		<p>NB: distinct from java.awt.Dialog
	 */

	public interface Dialog {
		/** Make dialog visible */
		void expose();

		/** Hide and destroy dialog */
		void dispose();

		/** Typically, the dialog will add a new button with the
			given label. */
		void      addAction(String cmd);

		/** Returns the text of the button used to dismiss the dialog */
		String    result();

		/** Returns the AWT container for this window, in to
			which components can be added in the normal way */
		Container container();
	}



	// .......................................................

	/** Sets the static current shell to point to the given shell */
	public static void setShell(Interface sh) { current=sh; }

	public static void print(String msg) { current.print(msg); }
	public static void status(String msg) { current.status(msg); }
	public static void trace(String msg) { current.trace(msg); }

	public static PrintWriter getPrintWriter() { return current.getPrintWriter(); }
	public static Window	getWindow(String title) { return current.getWindow(title); }
	public static Dialog	getDialog(String title) { return current.getDialog(title); }

	/** Create and return a new Panel containing the given Viewer */
	public static Viewer	createViewerPanel(Viewer v ) {
		return current.createViewerPanel(v);
	}

	/** Return an AWT Container containing buttons for each the
		commands reported by the given Agent */
	public static Container createButtonsFor(Agent a) {
		return current.createButtonsFor(a);
	}

	/** Return a label component. Can be AWT, Swing, or whatever */
	public static Component createLabel(String txt) {
		return current.createLabel(txt);
	}

	public static NumberViewer createNumberViewer( String l, int f, NumberSink dm) {
		return current.createNumberViewer(l,f,dm);
	}

	public static NumberViewer createNumberViewer( String l, int f) {
		return current.createNumberViewer(l,f,null);
	}

	// .......................................................
	private static boolean reg=true;

	/** If false, subsequent calls to <code>registerViewable()</code>
		 are ignored */
	public static boolean setAutoRegister(boolean f) { boolean old=reg; reg=f; return old; }
	public static void registerAgent(Agent a) { current.registerAgent(a); }
	public static void deregisterAgent(Agent a) {  current.deregisterAgent(a); }
	public static void registerViewable(Viewable v) { if (reg) current.registerViewable(v); }
	public static void deregisterViewable(Viewable v) { current.deregisterViewable(v); }
	public static void exposeCommands(Agent a) { current.exposeCommands(a); }
	public static void exit() { current.exit(); }

	// .......................................................
	// these two are for handling commands

	/** The interface for any kind of string interpreter */
	public interface Interpreter {
		/** Do something with no return value */
		void execute(Reader txt);

		/** Evaluate something and return a value
			(perhaps we should require with no side-effects?) */
		Object evaluate(Reader txt);
	}

	private static Interpreter interp=new Interpreter() {
		public void execute(Reader txt) { Shell.print("Sorry - don't know how"); }
		public Object evaluate(Reader txt) { throw new Error("no interpreter"); }
	};


	/** Set the current interpreter that is statically available */
	public static void setInterpreter(Interpreter i) { interp=i; }

	/** Use the current interepreter to interpret the given String */
	public static void interpret(String cmd) { interp.execute(new StringReader(cmd)); }
	/** Use the current interepreter to interpret commands from the Reader */
	public static void interpret(Reader in) { interp.execute(in); }



	// .......................................................
	// Environments

	private static Stack			stack=new Stack();		// environment stack
	private static Environment	_env=Environment.top();  		// current environment
	private static Interface		current=new DefaultShell();	// current shell

	/** Pops the top of the Environment stack. The one below
		becomes the new current environment. */
	public static void pop() { _env=(Environment)stack.pop(); }

	/** Push given Environment on top of stack. It becomes
		the new current environment. */
	public static void push(Environment e) { stack.push(_env); _env=e; }

	/** Pushes a new environment which contains no data but
		has the given base node. Used to establish a new node context */
	public static void push(Node n) { push(new Environment(_env,n)); }

	/** The same as <code>push(new Node(nm)) </code> ie, uses
		base node of current environment as a parent */
	public static void push(String nm) { push(new Environment(_env,nm)); }

	/** Return current node, the one at the top of the stack (without removing it)  */
	public static Environment env() { return _env; }

	// -------------------------------------------------------

	/** Get a double value from the current environment. Name
		can be absolute or relative to current base node. Returns def
		if no matching value is found */
	public static double getDouble(String nm, double def)    {
		return X._double(datum(nm),def);
	}

	/** See description for getDouble() */
	public static int		getInt(String nm, int def)          {
		return X._int(datum(nm),def);
	}

	/** See description for getDouble() */
	public static boolean getBoolean(String nm, boolean def) {
		return X._bool(datum(nm),def);
	}

	/** See description for getDouble() */
	public static String getString(String nm, String def)    {
		return X.string(datum(nm),def);
	}

	/** Get a String from current environment, throwing an Exception
		if no value is found. */
	public static String getString(String nm) throws Exception {
		return X.string(datum(nm));
	}

	public static Color	getColor(String nm, Color def)      {
		return X.color(datum(nm),def);
	}

	/** Search for named Datum in current Environment. */
	public static Datum   datum(String nm) { return _env.datum(nm); }

	/** Add and return new binding to current environment. */
	public static Binding put(String nm, Object o) { return _env.add(nm,o); }

	/** Search for and return object bound to given name. */
	public static Object  get(String nm) throws Exception {
		return _env.binding(nm).get();
	}

	/** Search for and return object bound to given name. */
	public static Object  get(String nm,Object def) {
		try { return _env.binding(nm).get();}
		catch (Exception ex) { return def; }
	}

	/** Remove named binding from current environment, ONLY if it
		is bound to the given object. */
	public static void remove(String nm, Object vl) {
		Binding v=_env.binding(nm);
		try { if (v.get()==vl) v.remove(); }
		catch (Exception ex) { Shell.trace("Shell.remove error: "+ex); }
	}


	public static void exposeCommands(final Object[] cmds)
	{
		// create a temporary Agent to manage the array of command names
		exposeCommands( new Agent() {
			public void getCommands(Agent.Registry r) {
				r.setTarget(null);
				for (int i=0; i<cmds.length; i++) r.add((String)cmds[i]);
			}
			public void execute(String cmd, Environment env) {}
		} );
	}

	// .......................................................
	// helper functions for creating windows and dialogs


	/** Creates and shows a dialog containing the given component
		and "ok" and "cancel" buttons. Returns "ok" or "cancel"
		depending on which button was pressed. */

	public static String showDialogFor(Component comp, String name)
	{
		Dialog	dlg = Shell.getDialog(name);

		dlg.container().add(comp);
		dlg.addAction("cancel");
		dlg.addAction("ok");
		dlg.expose();
		dlg.dispose();

		return dlg.result();
	}



	/**
		Expose the given Viewer. Uses the Shell.getWindow() to get
		some screen space, and adds the Viewer's component into the
		Shell.Window's container.
	  */

	public static Window showViewer( Viewer vwr, String name) {
		return expose(vwr.getComponent(),name);
	}

	/** same as showViewer() */
	public static Window expose( Viewer vwr, String name) {
		return expose(vwr.getComponent(),name);
	}

	
	/**
		Expose the given AWT Component. Uses the Shell.getWindow() to get
		some screen space, and adds the component into the
		Shell.Window's container. The window is given the supplied name.
	  */

	public static Window expose( Component c, String name) {
		Component [] cs = {c};
		return expose(cs,name);
	}

	/**
		Expose a bunch of Components in one window with the given name.
		The layout is the default one determined by the current shell's implementation
		of getWindow().
	  */
	public static Window expose( Component[] cs, String name)
	{
		Window win = Shell.getWindow(name);
		ViewerWindowListener l=new ViewerWindowListener(win,null); // vwr);

		//win.container().setLayout(new BoxLayout());
		//win.container().setLayout(new BorderLayout());
		// win.container().add(c,BorderLayout.CENTER);
		if (cs!=null) for (int i=0; i<cs.length; i++) win.container().add(cs[i]);
		win.container().addContainerListener(l);
		win.addWindowListener(l);
		win.expose();
		return win;
	}


	/**
		Completely removes all trace of the given viewer. Works by calling
		its detach() method	to relinquish any resources and drop
		references to itself, and removing it from its parent container
		if it has one.
	  */

	public static void releaseViewer(Viewer vwr)
	{
		vwr.detach();
		Component c=vwr.getComponent();
		Container p=c.getParent();
		if (p!=null) p.remove(c);
	}



	/**
		A singleton WindowListener that, when attached to a Window, calls
		Shell.exit() when the window is closed.
	 */

	public static WindowListener exitListener() { return exitter; }
	private static WindowListener exitter = new WindowAdapter() {
		public void windowClosing(WindowEvent e) { Shell.exit(); }
	};



	// Private listener class for handling a window containing a viewer

	public static WindowListener closeListener(Window w) { return new ViewerWindowListener(w,null); }
	private static class ViewerWindowListener extends WindowAdapter implements ContainerListener
	{
		Viewer	vwr;
		Window	win;
		public ViewerWindowListener(Window w, Viewer v) { win=w; vwr=v; }
		public void windowClosing(WindowEvent e) { if (vwr!=null) vwr.detach(); win.dispose(); }
		public void componentAdded(ContainerEvent e) {}
		public void componentRemoved(ContainerEvent e) {
			if (win.container().getComponentCount()>0) win.container().validate();
			else EventQueue.invokeLater( new Runnable() { public void run() { win.dispose(); }});
		}
	}
}




/**
	A fairly minimal implementation of Shell.Interface
	Much of the functionality is missing:
	<ul>
	<li>Implementations of print, status
	and trace go directly to System.out, System.out and System.err respectively,
	hence usually to stdout and stderr.

	<li> Window, dialog, button, label, Viewer creation functions all return null.
	<li> Register agent or viewable does nothing.
	<lI> exit() exits the JVM by calling System.exit()
	</ul>
 */

class DefaultShell implements Shell.Interface
{
	private PrintWriter writer=null;
	private PrintWriter stdout=new PrintWriter(System.out,true); // with autoflush
	private PrintWriter stderr=new PrintWriter(System.err,true); // with autoflush

	//	DefaultShell() { trace("creating DefaultShell"); }

	// Different types of message
	public void print(String string) { stdout.println(string); }
	public void status(String string) { stdout.println(string); }
	public void trace(String string) {	stderr.println(string); }

	public PrintWriter getPrintWriter()
	{
		if (writer==null)
			writer = new PrintWriter( new OutputStreamWriter(System.out));
		return writer;
	};

	public Shell.Dialog getDialog(String title) { return null; }
	public Shell.Window getWindow(String title) { return null; }
	public Container	createButtonsFor(Agent agent) { return null; }
	public Viewer		createViewerPanel(Viewer vwr) { return null; }
	public Component	createLabel(String nm) { return null; }
	public NumberViewer createNumberViewer( String l, int f, NumberSink s) { return null; }

	// registry
	public void registerAgent(Agent a) {}
	public void deregisterAgent(Agent a) {}
	public void registerViewable(Viewable v) {}
	public void deregisterViewable(Viewable v) {}
	public void exposeCommands(Agent agent) {}
	public void exit() { System.exit(0); }
}