/* Author: Jeff Dalton * Updated: Sat Dec 1 00:24:17 2001 by Jeff Dalton * Copyright: (c) 2001, AIAI, University of Edinburgh */ package ix.icore; import ix.iface.util.XML; // just for debugging output import ix.iface.util.Reporting; import ix.util.*; /** * Common class for I-X agents.

* * An agent is usually created by instantiating a subclass of IXAgent * and then calling its mainStartup(String[] argv) method. * That method will usually be inherited from IXAgent; it processes any * command line arguments and then calls startup(). * Thus, from the subclass's point of view, startup and initialization * has the following steps: *

    *
  1. One of the subclass's constructurs, which will call superclass * constructors. *
  2. mainStartup(String[] argv), which calls *
      *
    1. processCommandLineArguments() *
    2. startup() *
    *
* A subclass that redefines processCommandLineArguments() * or startup() should normally have them call the * corresponding super method to ensure that any * common processing is performed. For example, the IXAgent * processCommandLineArguments() method will set up * interprocess communication as specified by the argument * -ipc=methodName.

* * To support IPC, a subclass of IXAgent must define the * getAgentIPCName() method and will typically want * to define *

* or, to intervene at an earlier stage, the method that calls the * two above, * * * @see ix.test.SimpleIXAgent * @see SimpleIXAgent.java * source code */ public abstract class IXAgent { // The dreaded global variable ... protected static IXAgent thisAgent = null; /** * Standard constructor.

*/ public IXAgent() { if (thisAgent != null) throw new Error("Attempt to create multiple IX Agents in one VM"); else thisAgent = this; // /\/: Theme change doesn't work if done after objects created, // so is done here rather than in startup(). // ix.iface.Iface.adjustLookAndFeel(); } /** * Method called by main(String[] argv) to do initialization common * to all I-X agents. */ public void mainStartup(String[] argv) { // Parse command-line arguments Parameters.processCommandLineArguments(argv); // Have agent to look at any arguments it's interested in. processCommandLineArguments(); // All command-line arguments should now have been processed. Parameters.checkParameterUse(); // Finish setting things up startup(); } /** * Method called by the mainStartup(String[] argv) method * to perform any setup and initialization that should take place after * this agent's constructor has been called and command-line arguments * have been processed.

* * At present, this method does nothing, and all the work is done * in subclasses. */ protected void startup() { } /** * Handles command-line arguments common to all I-X agents. *

     *    -debug=boolean
     *    -fontsize=int
     *    -ipc=name
     * 
* At present, fontsize is ignored.

* debug is used to set Debug.on.

* The name in -ipc=name argument will be * interpreted by the IPC.getCommunicationStrategy(String) * method. * *

At present, this method also makes any standard I-X changes * to the look and feel. * * @see ix.iface.Iface#adjustLookAndFeel() * @see ix.util.IPC#getCommunicationStrategy(String methodName) * @see ix.util.Debug#on * @see ix.util.Parameters */ protected void processCommandLineArguments() { ix.iface.Iface.adjustLookAndFeel(); if (Parameters.haveParameter("fontsize")) DefaultFont.setDefaultFontSize( Parameters.getInt("fontsize")); Debug.on = Parameters.getBoolean("debug", true); if (Parameters.haveParameter("ipc")) startServer(getAgentIPCName(), Parameters.getParameter("ipc")); } /** * Returns an object that represents the agent. * At present, there can be only one IXAgent per VM. */ public static IXAgent getAgent() { return thisAgent; } /** * Returns the object used to represent the agent as an IPC "destination". * This object is usually, but not necessarily, a string containing the * agent's name. The details will depend on the set of communication * strategies that might be used. * * @see ix.util.IPC * @see ix.util.IPC.CommunicationStrategy */ public abstract Object getAgentIPCName(); /** * Returns this agent's symbol name. */ public Object getAgentSymbolName() { // /\/: Explain "symbol name" // /\/: Why not abstract? // /\/: Will symbol name and ipc name merge? return getAgentIPCName(); } /** * Set the default IPC communication strategy and set up * to receive messages. * * @see ix.util.IPC */ protected void startServer(Object agentName, String strategyName) { // Create the communication strategy. IPC.setDefaultCommunicationStrategy(strategyName); // Set up to receive messages. IPC.setupServer(agentName, new IPC.MessageListener() { public synchronized void messageReceived(IPC.InputMessage message) { pre_handleInput(message); } }); } /** * Gets the message before the handleInput method * and ensures that handleInput is called in the * AWT event dispatching thread. This is perhaps a temporary * measure. */ protected void pre_handleInput(final IPC.InputMessage message) { // /\/: We have to do this somewhere, and it's awkward // to do it anywhere else for reports. So for now ... javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { handleInput(message); } }); } /** * Handles external input in the form of an IPC.InputMessage * that contains an Issue or Report object. * * It calls handleNewIssue or handleNewReport * as appropriate. */ public void handleInput(IPC.InputMessage message) { Object contents = message.getContents(); if (contents instanceof Issue) { handleNewIssue((Issue)contents); } else if (contents instanceof Report) { handleNewReport((Report)contents); } else { displayMessage("Unexpected message contents " + contents); } } /** * Handles new issues from external sources. Subclasses will * usually redefine this method. */ public void handleNewIssue(Issue issue) { displayMessage(Reporting.issueDescription(issue)); } /** * Handles new reports from external sources. Subclasses will * usually redefine this method. */ public void handleNewReport(Report report) { displayMessage(Reporting.reportDescription(report)); } /* * Default message display */ /** * A text area in a separate frame used to display information about * incoming messages. */ protected TextAreaFrame textFrame = null; /** * Adds the specified string to the default message display. */ protected void displayMessage(String message) { if (textFrame == null) textFrame = new TextAreaFrame("Messages to " + getAgentIPCName()); textFrame.appendLine(message); } } // Issues: // * Should keep a record of how IXAgent.getAgent() is used. // * If a startup() method makes the GUI visible, subclass startup() // methods that call super.startup() will need to know this so they can // ensure that subsequent GUI stuff happens in the event-handling thread. // It might be better to have a separate makeVisible() that happens // after startup(); and maybe startup() should be renamed to something // like completeSetup().