samer@0
|
1 /*
|
samer@0
|
2 * Shell.java
|
samer@0
|
3 *
|
samer@0
|
4 * Copyright (c) 2000, Samer Abdallah, King's College London.
|
samer@0
|
5 * All rights reserved.
|
samer@0
|
6 *
|
samer@0
|
7 * This software is provided AS IS and WITHOUT ANY WARRANTY;
|
samer@0
|
8 * without even the implied warranty of MERCHANTABILITY or
|
samer@0
|
9 * FITNESS FOR A PARTICULAR PURPOSE.
|
samer@0
|
10 */
|
samer@0
|
11
|
samer@0
|
12 package samer.core;
|
samer@0
|
13 import java.io.*;
|
samer@0
|
14 import java.awt.*;
|
samer@0
|
15 import java.awt.event.*;
|
samer@0
|
16 import java.util.*;
|
samer@0
|
17
|
samer@0
|
18 import samer.core.Environment.Datum;
|
samer@0
|
19 import samer.core.Environment.Binding;
|
samer@0
|
20 import samer.core.Environment.Autocoder;
|
samer@0
|
21
|
samer@0
|
22
|
samer@0
|
23
|
samer@0
|
24 /**
|
samer@0
|
25 This class provides a set of static methods for
|
samer@0
|
26 some useful services. The actual implementation is
|
samer@0
|
27 provided by some object that implements the
|
samer@0
|
28 Shell.Interface interface.
|
samer@0
|
29 */
|
samer@0
|
30
|
samer@0
|
31 public class Shell
|
samer@0
|
32 {
|
samer@0
|
33 /** All shells must implement this interface. It provides service that
|
samer@0
|
34 * are generally useful but can be implemented in different ways, such
|
samer@0
|
35 * as printing messages and getting usable windows. */
|
samer@0
|
36
|
samer@0
|
37 public interface Interface
|
samer@0
|
38 {
|
samer@0
|
39 // different types of message
|
samer@0
|
40 void print(String msg);
|
samer@0
|
41 void status(String msg);
|
samer@0
|
42 void trace(String msg);
|
samer@0
|
43
|
samer@0
|
44 // factory methods
|
samer@0
|
45 PrintWriter getPrintWriter();
|
samer@0
|
46 Window getWindow(String title);
|
samer@0
|
47 Dialog getDialog(String title);
|
samer@0
|
48
|
samer@0
|
49 Container createButtonsFor(Agent agent);
|
samer@0
|
50 Viewer createViewerPanel(Viewer vwr);
|
samer@0
|
51 Component createLabel(String nm);
|
samer@0
|
52
|
samer@0
|
53 NumberViewer createNumberViewer( String label, int flags, NumberSink s);
|
samer@0
|
54
|
samer@0
|
55 // agents and viewables
|
samer@0
|
56 void registerViewable(Viewable v);
|
samer@0
|
57 void deregisterViewable(Viewable v);
|
samer@0
|
58 void registerAgent(Agent a);
|
samer@0
|
59 void deregisterAgent(Agent a);
|
samer@0
|
60 void exposeCommands(Agent agent);
|
samer@0
|
61 void exit();
|
samer@0
|
62 }
|
samer@0
|
63
|
samer@0
|
64
|
samer@0
|
65 /**
|
samer@0
|
66 Interface returned by the getWindow method of the
|
samer@0
|
67 Shell.Interface interface. The window is not necessarily
|
samer@0
|
68 visible on return -- use the <code>expose()</code>
|
samer@0
|
69 method to make it so.
|
samer@0
|
70 NB: distinct from java.awt.Window.
|
samer@0
|
71 */
|
samer@0
|
72
|
samer@0
|
73 public interface Window {
|
samer@0
|
74 /** Make window visible */
|
samer@0
|
75 void expose();
|
samer@0
|
76
|
samer@0
|
77 /** Hide and destroy window */
|
samer@0
|
78 void dispose();
|
samer@0
|
79
|
samer@0
|
80 /** Should behave just like in <code>java.awt.Window</code> */
|
samer@0
|
81 void addWindowListener(WindowListener wl);
|
samer@0
|
82
|
samer@0
|
83 /** Returns the AWT container for this window, in to
|
samer@0
|
84 which components can be added in the normal way */
|
samer@0
|
85 Container container();
|
samer@0
|
86 }
|
samer@0
|
87
|
samer@0
|
88 /**
|
samer@0
|
89 Interface returned by the getDialog method of the
|
samer@0
|
90 Shell.Interface interface. The dialog is not necessarily
|
samer@0
|
91 visible on return -- use the <code>expose()</code>
|
samer@0
|
92 method to make it so.
|
samer@0
|
93 <p>A dialog is distinct from a window in that it
|
samer@0
|
94 is modal, and is dismissed by one of a set of buttons.
|
samer@0
|
95 <p>NB: distinct from java.awt.Dialog
|
samer@0
|
96 */
|
samer@0
|
97
|
samer@0
|
98 public interface Dialog {
|
samer@0
|
99 /** Make dialog visible */
|
samer@0
|
100 void expose();
|
samer@0
|
101
|
samer@0
|
102 /** Hide and destroy dialog */
|
samer@0
|
103 void dispose();
|
samer@0
|
104
|
samer@0
|
105 /** Typically, the dialog will add a new button with the
|
samer@0
|
106 given label. */
|
samer@0
|
107 void addAction(String cmd);
|
samer@0
|
108
|
samer@0
|
109 /** Returns the text of the button used to dismiss the dialog */
|
samer@0
|
110 String result();
|
samer@0
|
111
|
samer@0
|
112 /** Returns the AWT container for this window, in to
|
samer@0
|
113 which components can be added in the normal way */
|
samer@0
|
114 Container container();
|
samer@0
|
115 }
|
samer@0
|
116
|
samer@0
|
117
|
samer@0
|
118
|
samer@0
|
119 // .......................................................
|
samer@0
|
120
|
samer@0
|
121 /** Sets the static current shell to point to the given shell */
|
samer@0
|
122 public static void setShell(Interface sh) { current=sh; }
|
samer@0
|
123
|
samer@0
|
124 public static void print(String msg) { current.print(msg); }
|
samer@0
|
125 public static void status(String msg) { current.status(msg); }
|
samer@0
|
126 public static void trace(String msg) { current.trace(msg); }
|
samer@0
|
127
|
samer@0
|
128 public static PrintWriter getPrintWriter() { return current.getPrintWriter(); }
|
samer@0
|
129 public static Window getWindow(String title) { return current.getWindow(title); }
|
samer@0
|
130 public static Dialog getDialog(String title) { return current.getDialog(title); }
|
samer@0
|
131
|
samer@0
|
132 /** Create and return a new Panel containing the given Viewer */
|
samer@0
|
133 public static Viewer createViewerPanel(Viewer v ) {
|
samer@0
|
134 return current.createViewerPanel(v);
|
samer@0
|
135 }
|
samer@0
|
136
|
samer@0
|
137 /** Return an AWT Container containing buttons for each the
|
samer@0
|
138 commands reported by the given Agent */
|
samer@0
|
139 public static Container createButtonsFor(Agent a) {
|
samer@0
|
140 return current.createButtonsFor(a);
|
samer@0
|
141 }
|
samer@0
|
142
|
samer@0
|
143 /** Return a label component. Can be AWT, Swing, or whatever */
|
samer@0
|
144 public static Component createLabel(String txt) {
|
samer@0
|
145 return current.createLabel(txt);
|
samer@0
|
146 }
|
samer@0
|
147
|
samer@0
|
148 public static NumberViewer createNumberViewer( String l, int f, NumberSink dm) {
|
samer@0
|
149 return current.createNumberViewer(l,f,dm);
|
samer@0
|
150 }
|
samer@0
|
151
|
samer@0
|
152 public static NumberViewer createNumberViewer( String l, int f) {
|
samer@0
|
153 return current.createNumberViewer(l,f,null);
|
samer@0
|
154 }
|
samer@0
|
155
|
samer@0
|
156 // .......................................................
|
samer@0
|
157 private static boolean reg=true;
|
samer@0
|
158
|
samer@0
|
159 /** If false, subsequent calls to <code>registerViewable()</code>
|
samer@0
|
160 are ignored */
|
samer@0
|
161 public static boolean setAutoRegister(boolean f) { boolean old=reg; reg=f; return old; }
|
samer@0
|
162 public static void registerAgent(Agent a) { current.registerAgent(a); }
|
samer@0
|
163 public static void deregisterAgent(Agent a) { current.deregisterAgent(a); }
|
samer@0
|
164 public static void registerViewable(Viewable v) { if (reg) current.registerViewable(v); }
|
samer@0
|
165 public static void deregisterViewable(Viewable v) { current.deregisterViewable(v); }
|
samer@0
|
166 public static void exposeCommands(Agent a) { current.exposeCommands(a); }
|
samer@0
|
167 public static void exit() { current.exit(); }
|
samer@0
|
168
|
samer@0
|
169 // .......................................................
|
samer@0
|
170 // these two are for handling commands
|
samer@0
|
171
|
samer@0
|
172 /** The interface for any kind of string interpreter */
|
samer@0
|
173 public interface Interpreter {
|
samer@0
|
174 /** Do something with no return value */
|
samer@0
|
175 void execute(Reader txt);
|
samer@0
|
176
|
samer@0
|
177 /** Evaluate something and return a value
|
samer@0
|
178 (perhaps we should require with no side-effects?) */
|
samer@0
|
179 Object evaluate(Reader txt);
|
samer@0
|
180 }
|
samer@0
|
181
|
samer@0
|
182 private static Interpreter interp=new Interpreter() {
|
samer@0
|
183 public void execute(Reader txt) { Shell.print("Sorry - don't know how"); }
|
samer@0
|
184 public Object evaluate(Reader txt) { throw new Error("no interpreter"); }
|
samer@0
|
185 };
|
samer@0
|
186
|
samer@0
|
187
|
samer@0
|
188 /** Set the current interpreter that is statically available */
|
samer@0
|
189 public static void setInterpreter(Interpreter i) { interp=i; }
|
samer@0
|
190
|
samer@0
|
191 /** Use the current interepreter to interpret the given String */
|
samer@0
|
192 public static void interpret(String cmd) { interp.execute(new StringReader(cmd)); }
|
samer@0
|
193 /** Use the current interepreter to interpret commands from the Reader */
|
samer@0
|
194 public static void interpret(Reader in) { interp.execute(in); }
|
samer@0
|
195
|
samer@0
|
196
|
samer@0
|
197
|
samer@0
|
198 // .......................................................
|
samer@0
|
199 // Environments
|
samer@0
|
200
|
samer@0
|
201 private static Stack stack=new Stack(); // environment stack
|
samer@0
|
202 private static Environment _env=Environment.top(); // current environment
|
samer@0
|
203 private static Interface current=new DefaultShell(); // current shell
|
samer@0
|
204
|
samer@0
|
205 /** Pops the top of the Environment stack. The one below
|
samer@0
|
206 becomes the new current environment. */
|
samer@0
|
207 public static void pop() { _env=(Environment)stack.pop(); }
|
samer@0
|
208
|
samer@0
|
209 /** Push given Environment on top of stack. It becomes
|
samer@0
|
210 the new current environment. */
|
samer@0
|
211 public static void push(Environment e) { stack.push(_env); _env=e; }
|
samer@0
|
212
|
samer@0
|
213 /** Pushes a new environment which contains no data but
|
samer@0
|
214 has the given base node. Used to establish a new node context */
|
samer@0
|
215 public static void push(Node n) { push(new Environment(_env,n)); }
|
samer@0
|
216
|
samer@0
|
217 /** The same as <code>push(new Node(nm)) </code> ie, uses
|
samer@0
|
218 base node of current environment as a parent */
|
samer@0
|
219 public static void push(String nm) { push(new Environment(_env,nm)); }
|
samer@0
|
220
|
samer@0
|
221 /** Return current node, the one at the top of the stack (without removing it) */
|
samer@0
|
222 public static Environment env() { return _env; }
|
samer@0
|
223
|
samer@0
|
224 // -------------------------------------------------------
|
samer@0
|
225
|
samer@0
|
226 /** Get a double value from the current environment. Name
|
samer@0
|
227 can be absolute or relative to current base node. Returns def
|
samer@0
|
228 if no matching value is found */
|
samer@0
|
229 public static double getDouble(String nm, double def) {
|
samer@0
|
230 return X._double(datum(nm),def);
|
samer@0
|
231 }
|
samer@0
|
232
|
samer@0
|
233 /** See description for getDouble() */
|
samer@0
|
234 public static int getInt(String nm, int def) {
|
samer@0
|
235 return X._int(datum(nm),def);
|
samer@0
|
236 }
|
samer@0
|
237
|
samer@0
|
238 /** See description for getDouble() */
|
samer@0
|
239 public static boolean getBoolean(String nm, boolean def) {
|
samer@0
|
240 return X._bool(datum(nm),def);
|
samer@0
|
241 }
|
samer@0
|
242
|
samer@0
|
243 /** See description for getDouble() */
|
samer@0
|
244 public static String getString(String nm, String def) {
|
samer@0
|
245 return X.string(datum(nm),def);
|
samer@0
|
246 }
|
samer@0
|
247
|
samer@0
|
248 /** Get a String from current environment, throwing an Exception
|
samer@0
|
249 if no value is found. */
|
samer@0
|
250 public static String getString(String nm) throws Exception {
|
samer@0
|
251 return X.string(datum(nm));
|
samer@0
|
252 }
|
samer@0
|
253
|
samer@0
|
254 public static Color getColor(String nm, Color def) {
|
samer@0
|
255 return X.color(datum(nm),def);
|
samer@0
|
256 }
|
samer@0
|
257
|
samer@0
|
258 /** Search for named Datum in current Environment. */
|
samer@0
|
259 public static Datum datum(String nm) { return _env.datum(nm); }
|
samer@0
|
260
|
samer@0
|
261 /** Add and return new binding to current environment. */
|
samer@0
|
262 public static Binding put(String nm, Object o) { return _env.add(nm,o); }
|
samer@0
|
263
|
samer@0
|
264 /** Search for and return object bound to given name. */
|
samer@0
|
265 public static Object get(String nm) throws Exception {
|
samer@0
|
266 return _env.binding(nm).get();
|
samer@0
|
267 }
|
samer@0
|
268
|
samer@0
|
269 /** Search for and return object bound to given name. */
|
samer@0
|
270 public static Object get(String nm,Object def) {
|
samer@0
|
271 try { return _env.binding(nm).get();}
|
samer@0
|
272 catch (Exception ex) { return def; }
|
samer@0
|
273 }
|
samer@0
|
274
|
samer@0
|
275 /** Remove named binding from current environment, ONLY if it
|
samer@0
|
276 is bound to the given object. */
|
samer@0
|
277 public static void remove(String nm, Object vl) {
|
samer@0
|
278 Binding v=_env.binding(nm);
|
samer@0
|
279 try { if (v.get()==vl) v.remove(); }
|
samer@0
|
280 catch (Exception ex) { Shell.trace("Shell.remove error: "+ex); }
|
samer@0
|
281 }
|
samer@0
|
282
|
samer@0
|
283
|
samer@0
|
284 public static void exposeCommands(final Object[] cmds)
|
samer@0
|
285 {
|
samer@0
|
286 // create a temporary Agent to manage the array of command names
|
samer@0
|
287 exposeCommands( new Agent() {
|
samer@0
|
288 public void getCommands(Agent.Registry r) {
|
samer@0
|
289 r.setTarget(null);
|
samer@0
|
290 for (int i=0; i<cmds.length; i++) r.add((String)cmds[i]);
|
samer@0
|
291 }
|
samer@0
|
292 public void execute(String cmd, Environment env) {}
|
samer@0
|
293 } );
|
samer@0
|
294 }
|
samer@0
|
295
|
samer@0
|
296 // .......................................................
|
samer@0
|
297 // helper functions for creating windows and dialogs
|
samer@0
|
298
|
samer@0
|
299
|
samer@0
|
300 /** Creates and shows a dialog containing the given component
|
samer@0
|
301 and "ok" and "cancel" buttons. Returns "ok" or "cancel"
|
samer@0
|
302 depending on which button was pressed. */
|
samer@0
|
303
|
samer@0
|
304 public static String showDialogFor(Component comp, String name)
|
samer@0
|
305 {
|
samer@0
|
306 Dialog dlg = Shell.getDialog(name);
|
samer@0
|
307
|
samer@0
|
308 dlg.container().add(comp);
|
samer@0
|
309 dlg.addAction("cancel");
|
samer@0
|
310 dlg.addAction("ok");
|
samer@0
|
311 dlg.expose();
|
samer@0
|
312 dlg.dispose();
|
samer@0
|
313
|
samer@0
|
314 return dlg.result();
|
samer@0
|
315 }
|
samer@0
|
316
|
samer@0
|
317
|
samer@0
|
318
|
samer@0
|
319 /**
|
samer@0
|
320 Expose the given Viewer. Uses the Shell.getWindow() to get
|
samer@0
|
321 some screen space, and adds the Viewer's component into the
|
samer@0
|
322 Shell.Window's container.
|
samer@0
|
323 */
|
samer@0
|
324
|
samer@0
|
325 public static Window showViewer( Viewer vwr, String name) {
|
samer@0
|
326 return expose(vwr.getComponent(),name);
|
samer@0
|
327 }
|
samer@0
|
328
|
samer@0
|
329 /** same as showViewer() */
|
samer@0
|
330 public static Window expose( Viewer vwr, String name) {
|
samer@0
|
331 return expose(vwr.getComponent(),name);
|
samer@0
|
332 }
|
samer@0
|
333
|
samer@0
|
334
|
samer@0
|
335 /**
|
samer@0
|
336 Expose the given AWT Component. Uses the Shell.getWindow() to get
|
samer@0
|
337 some screen space, and adds the component into the
|
samer@0
|
338 Shell.Window's container. The window is given the supplied name.
|
samer@0
|
339 */
|
samer@0
|
340
|
samer@0
|
341 public static Window expose( Component c, String name) {
|
samer@0
|
342 Component [] cs = {c};
|
samer@0
|
343 return expose(cs,name);
|
samer@0
|
344 }
|
samer@0
|
345
|
samer@0
|
346 /**
|
samer@0
|
347 Expose a bunch of Components in one window with the given name.
|
samer@0
|
348 The layout is the default one determined by the current shell's implementation
|
samer@0
|
349 of getWindow().
|
samer@0
|
350 */
|
samer@0
|
351 public static Window expose( Component[] cs, String name)
|
samer@0
|
352 {
|
samer@0
|
353 Window win = Shell.getWindow(name);
|
samer@0
|
354 ViewerWindowListener l=new ViewerWindowListener(win,null); // vwr);
|
samer@0
|
355
|
samer@0
|
356 //win.container().setLayout(new BoxLayout());
|
samer@0
|
357 //win.container().setLayout(new BorderLayout());
|
samer@0
|
358 // win.container().add(c,BorderLayout.CENTER);
|
samer@0
|
359 if (cs!=null) for (int i=0; i<cs.length; i++) win.container().add(cs[i]);
|
samer@0
|
360 win.container().addContainerListener(l);
|
samer@0
|
361 win.addWindowListener(l);
|
samer@0
|
362 win.expose();
|
samer@0
|
363 return win;
|
samer@0
|
364 }
|
samer@0
|
365
|
samer@0
|
366
|
samer@0
|
367 /**
|
samer@0
|
368 Completely removes all trace of the given viewer. Works by calling
|
samer@0
|
369 its detach() method to relinquish any resources and drop
|
samer@0
|
370 references to itself, and removing it from its parent container
|
samer@0
|
371 if it has one.
|
samer@0
|
372 */
|
samer@0
|
373
|
samer@0
|
374 public static void releaseViewer(Viewer vwr)
|
samer@0
|
375 {
|
samer@0
|
376 vwr.detach();
|
samer@0
|
377 Component c=vwr.getComponent();
|
samer@0
|
378 Container p=c.getParent();
|
samer@0
|
379 if (p!=null) p.remove(c);
|
samer@0
|
380 }
|
samer@0
|
381
|
samer@0
|
382
|
samer@0
|
383
|
samer@0
|
384 /**
|
samer@0
|
385 A singleton WindowListener that, when attached to a Window, calls
|
samer@0
|
386 Shell.exit() when the window is closed.
|
samer@0
|
387 */
|
samer@0
|
388
|
samer@0
|
389 public static WindowListener exitListener() { return exitter; }
|
samer@0
|
390 private static WindowListener exitter = new WindowAdapter() {
|
samer@0
|
391 public void windowClosing(WindowEvent e) { Shell.exit(); }
|
samer@0
|
392 };
|
samer@0
|
393
|
samer@0
|
394
|
samer@0
|
395
|
samer@0
|
396 // Private listener class for handling a window containing a viewer
|
samer@0
|
397
|
samer@0
|
398 public static WindowListener closeListener(Window w) { return new ViewerWindowListener(w,null); }
|
samer@0
|
399 private static class ViewerWindowListener extends WindowAdapter implements ContainerListener
|
samer@0
|
400 {
|
samer@0
|
401 Viewer vwr;
|
samer@0
|
402 Window win;
|
samer@0
|
403 public ViewerWindowListener(Window w, Viewer v) { win=w; vwr=v; }
|
samer@0
|
404 public void windowClosing(WindowEvent e) { if (vwr!=null) vwr.detach(); win.dispose(); }
|
samer@0
|
405 public void componentAdded(ContainerEvent e) {}
|
samer@0
|
406 public void componentRemoved(ContainerEvent e) {
|
samer@0
|
407 if (win.container().getComponentCount()>0) win.container().validate();
|
samer@0
|
408 else EventQueue.invokeLater( new Runnable() { public void run() { win.dispose(); }});
|
samer@0
|
409 }
|
samer@0
|
410 }
|
samer@0
|
411 }
|
samer@0
|
412
|
samer@0
|
413
|
samer@0
|
414
|
samer@0
|
415
|
samer@0
|
416 /**
|
samer@0
|
417 A fairly minimal implementation of Shell.Interface
|
samer@0
|
418 Much of the functionality is missing:
|
samer@0
|
419 <ul>
|
samer@0
|
420 <li>Implementations of print, status
|
samer@0
|
421 and trace go directly to System.out, System.out and System.err respectively,
|
samer@0
|
422 hence usually to stdout and stderr.
|
samer@0
|
423
|
samer@0
|
424 <li> Window, dialog, button, label, Viewer creation functions all return null.
|
samer@0
|
425 <li> Register agent or viewable does nothing.
|
samer@0
|
426 <lI> exit() exits the JVM by calling System.exit()
|
samer@0
|
427 </ul>
|
samer@0
|
428 */
|
samer@0
|
429
|
samer@0
|
430 class DefaultShell implements Shell.Interface
|
samer@0
|
431 {
|
samer@0
|
432 private PrintWriter writer=null;
|
samer@0
|
433 private PrintWriter stdout=new PrintWriter(System.out,true); // with autoflush
|
samer@1
|
434 private PrintWriter stderr=new PrintWriter(System.err,true); // with autoflush
|
samer@0
|
435
|
samer@0
|
436 // DefaultShell() { trace("creating DefaultShell"); }
|
samer@0
|
437
|
samer@0
|
438 // Different types of message
|
samer@0
|
439 public void print(String string) { stdout.println(string); }
|
samer@0
|
440 public void status(String string) { stdout.println(string); }
|
samer@0
|
441 public void trace(String string) { stderr.println(string); }
|
samer@0
|
442
|
samer@0
|
443 public PrintWriter getPrintWriter()
|
samer@0
|
444 {
|
samer@0
|
445 if (writer==null)
|
samer@0
|
446 writer = new PrintWriter( new OutputStreamWriter(System.out));
|
samer@0
|
447 return writer;
|
samer@0
|
448 };
|
samer@0
|
449
|
samer@0
|
450 public Shell.Dialog getDialog(String title) { return null; }
|
samer@0
|
451 public Shell.Window getWindow(String title) { return null; }
|
samer@0
|
452 public Container createButtonsFor(Agent agent) { return null; }
|
samer@0
|
453 public Viewer createViewerPanel(Viewer vwr) { return null; }
|
samer@0
|
454 public Component createLabel(String nm) { return null; }
|
samer@0
|
455 public NumberViewer createNumberViewer( String l, int f, NumberSink s) { return null; }
|
samer@0
|
456
|
samer@0
|
457 // registry
|
samer@0
|
458 public void registerAgent(Agent a) {}
|
samer@0
|
459 public void deregisterAgent(Agent a) {}
|
samer@0
|
460 public void registerViewable(Viewable v) {}
|
samer@0
|
461 public void deregisterViewable(Viewable v) {}
|
samer@0
|
462 public void exposeCommands(Agent agent) {}
|
samer@0
|
463 public void exit() { System.exit(0); }
|
samer@0
|
464 }
|