/* Author: Jeff Dalton * Updated: Fri Apr 13 01:06:57 2001 by Jeff Dalton * Copyright: (c) 2000, AIAI, University of Edinburgh */ package ix.examples; import java.util.*; import ix.util.*; import ix.util.lisp.*; /** * A simple example of an I-X framework. */ public class PicoIX { /** * Our issues contain a verb and an object. */ static class Issue { Object verb = Lisp.NIL; // NIL's a printable null Object object = Lisp.NIL; // NIL's a printable null Issue(Object verb, Object object) { this.verb = verb; this.object = object; } Issue(Object verb) { this.verb = verb; } public String toString() { return "issue[" + Lisp.printToString(verb) + ", " + Lisp.printToString(object) + "]"; } } /** * A class that provides some basic structure, and some support * methods, for issue handlers. Issue handlers are created by * instantiating a subclass. */ static abstract class IssueHandler { Object verb; IX_System system; IssueHandler(Object verb) { this.verb = verb; } void setSystem(IX_System system) { this.system = system; } abstract void handleIssue(Issue i); // Handy methods for use in issue handlers. void postIssue(Issue i) { system.controller.addIssue(i); } Object addConstraint(Constraint c) { return system.model.addConstraint(c); } Object tryConstraint(Constraint c) { return system.model.tryConstraint(c); } } /** * A simple, generic structure for constraints. */ static class Constraint { Object type; // for selecting the CM Object args; // perhaps just for toString() and debugging Constraint(Object type) { this.type = type; } public String toString() { return "issue[" + Lisp.printToString(type) + ", " + Lisp.printToString(args) + "]"; } } /** * A class that provides some basic structure, and some support * methods, for constraint managers. Constraint managers are * created by instantiating a subclass. */ static abstract class ConstraintManager { IX_System system; Object type; ConstraintManager(Object type) { this.type = type; } void setSystem(IX_System system) { this.system = system; } abstract Object addConstraint(Constraint c); abstract Object tryConstraint(Constraint c); // Handy methods for use in constraint managers // (We don't yet have any.) } /** * A simple constraint model. */ static class ModelManager { IX_System system; Hashtable CMTable = new Hashtable(); ModelManager() {} ModelManager(IX_System system) { setSystem(system); } void setSystem(IX_System system) { this.system = system; } void installConstraintManagers(Object[] ConstraintManagers) { // Put the constraint managers in the table // and tell each of them about their containing system for (Enumeration ce = Seq.elements(ConstraintManagers); ce.hasMoreElements();) { ConstraintManager cm = (ConstraintManager)ce.nextElement(); cm.setSystem(system); CMTable.put(cm.type, cm); } } Object addConstraint(Constraint c) { return findCM(c).addConstraint(c); } Object tryConstraint(Constraint c) { return findCM(c).tryConstraint(c); } ConstraintManager findCM(Constraint c) { ConstraintManager cm = (ConstraintManager)CMTable.get(c.type); Debug.assert(cm != null, "no CM for", cm.type); return cm; } } /** * Information is sent out via a listener. */ static class IX_SystemListener { public IX_SystemListener() {} void receive(Object message) { Debug.noteln("Listener received:", message); } } /** * A simple I-X entity with its own thread. */ static class IX_System implements Runnable { Thread thread = new Thread(this); MessageQueue q = new MessageQueue(); Controller controller; ModelManager model; IX_SystemListener listener; IX_System() { controller = new Controller(this); model = new ModelManager(this); } IX_System(Controller c, ModelManager a) { this.controller = c != null ? c : new Controller(); this.model = a != null ? a : new ModelManager(); this.controller.setSystem(this); this.model.setSystem(this); } void addIssueHandlers(Object[] issueHandlers) { controller.installIssueHandlers(issueHandlers); } void addConstraintManagers(Object[] constraintManagers) { model.installConstraintManagers(constraintManagers); } public void setListener(IX_SystemListener listener) { this.listener = listener; } void notifyListener(Object message) { if (listener != null) listener.receive(message); else Debug.noteln("No listener for", message); } public void newEvent(Object e) { q.send(e); } Issue eventToIssue(Object e) { return (Issue)e; } public void start() { thread.start(); } public void stop() { thread.stop(); } public void run() { controller.mainLoop(); } } /** * A very simple controller. */ static class Controller { IX_System system; MessageQueue q; LListCollector issues = new LListCollector(); Hashtable handlerTable = new Hashtable(); Controller() {} Controller(IX_System system) { setSystem(system); } void setSystem(IX_System system) { this.system = system; this.q = system.q; } void mainLoop() { // Do this forever. while (true) { // Convert any events to Issues. while (q.hasMessage()) { addEventIssue(q); } // If there's an issue, handle it; // otherwise wait for events. if (!issues.isEmpty()) { handleIssue(selectIssue()); } else { q.waitForMessage(); } } } protected void addIssue(Issue i) { Debug.noteln("Adding", i); issues.addElement(i); } protected void addEventIssue(MessageQueue q) { // N.B. the system translates the event to an issue. addIssue(system.eventToIssue(q.nextMessage())); } Issue selectIssue() { return (Issue)issues.popElement(); } void installIssueHandlers(Object[] issueHandlers) { installIssueHandlers(issueHandlers, handlerTable); } void installIssueHandlers(Object[] handlers, Hashtable table) { // Put the handlers into table, and tell each of them // about their containing system. for (Enumeration he = Seq.elements(handlers); he.hasMoreElements();) { IssueHandler h = (IssueHandler)he.nextElement(); h.setSystem(system); table.put(h.verb, h); } } IssueHandler findHandler(Issue i) { IssueHandler h = (IssueHandler)handlerTable.get(i.verb); Debug.assert(h != null, "no handler for", i.verb); return h; } void handleIssue(Issue i) { Debug.noteln("Handling", i); findHandler(i).handleIssue(i); } } } // Issues: // * handler.getKey() or handler.verb directly? If the former, issues // should have getKey() too, and similarly for constraints and CMs. // A Support class might be defined with a static method for putting // such things into tables. They'd have to implement a common // interface class.