All Downloads are FREE. Search and download functionalities are using the official Maven repository.

acm.program.Program Maven / Gradle / Ivy

Go to download

This the original Stanford Karel for Java, packaged for Maven. ACM Library is included. See also https://cs.stanford.edu/people/eroberts/karel-the-robot-learns-java.pdf

The newest version!
/*
 * @(#)Program.java   1.99.1 08/12/08
 */

// ************************************************************************
// * Copyright (c) 2008 by the Association for Computing Machinery        *
// *                                                                      *
// * The Java Task Force seeks to impose few restrictions on the use of   *
// * these packages so that users have as much freedom as possible to     *
// * use this software in constructive ways and can make the benefits of  *
// * that work available to others.  In view of the legal complexities    *
// * of software development, however, it is essential for the ACM to     *
// * maintain its copyright to guard against attempts by others to        *
// * claim ownership rights.  The full text of the JTF Software License   *
// * is available at the following URL:                                   *
// *                                                                      *
// *          http://www.acm.org/jtf/jtf-software-license.pdf             *
// *                                                                      *
// ************************************************************************

// REVISION HISTORY
//
// -- V2.0 --
// Bug fix 30-Jan-07 (ESR, JTFBug 2007-002)
//   1. Change default in getCommandLine to behave as if Linux.
//   2. If environment is headless, substitute a CommandLineProgram.
//
// Feature enhancement 2-Mar-07 (ESR)
//   1. Added menu option to export the program as an applet.
//   2. Added menu option to submit the program via email.
//
// Bug fix 8-May-07 (ESR, JTFBug 2007-007)
//   1. Fixed significant bug introduced by JDK 1.6 in which it is no
//      longer possible to display a JApplet as a component.
//
// Code cleanup 28-May-07 (ESR)
//   1. Added generic type tags.
//   2. Rewrote code for handling command-line arguments.
//   3. Repackaged the ButtonLike interface.
//   4. Added setInputModel, setOutputModel, and setDialog for symmetry
//
// Bug fix 10-Sep-07 (ESR, JTFBug 2007-012)
//   1. Fixed deadlock bug arising from change to thread handling in JDK 1.6.
//
// Code cleanup 10-May-08 (ESR)
//   1. Changed code to account for introduction of CommandLineProgram class.
//
// Bug fix 21-May-08 (ESR, JTFBug 2008-002)
//   1. Fixed the logic for isAppletMode.
//
// Code cleanup 21-May-08 (ESR)
//   1. Changed factory method for the menu bar to pass the program.
//   2. Redesigned other code features to account for redesign of the
//      ProgramMenuBar class.
//
// Bug fix 10-Jun-08 (ESR, JTFBug 2008-003)
//   1. Fixed serious bug caused by overriding the definitions of
//      getWidth and getHeight (which was, in retrospect, a poor design).
//      To avoid requiring changes to client code, the implementation now
//      checks to see whether these methods have been called from inside
//      the java package hierarchy and, if so, maintains their traditional
//      semantics.

package acm.program;

import acm.gui.*;
import acm.io.*;
import acm.util.*;
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;
import java.lang.reflect.*;
import java.net.*;
import java.util.*;
import javax.swing.*;

/* Class: Program */
/**
 * This class is the superclass for all executable
 * programs in the acm.program package.  Its principal
 * role is to unify the concepts of applets and applications in a single
 * class, although it also provides applications with many other useful
 * facilities not traditionally available in applications.
 *
 * 

 

In many programming environments, objects that are specific instances * of a Program subclass will run automatically without any * special action on your part. For maximum portability, you might want * to define a static main method as described in the comments * for the standard implementation of main. */ public abstract class Program extends JApplet implements IOModel, Runnable, MouseListener, MouseMotionListener, KeyListener, ActionListener { /** Constant specifying the north edge of the container */ public static final String NORTH = BorderLayout.NORTH; /** Constant specifying the south edge of the container */ public static final String SOUTH = BorderLayout.SOUTH; /** Constant specifying the east edge of the container */ public static final String EAST = BorderLayout.EAST; /** Constant specifying the west edge of the container */ public static final String WEST = BorderLayout.WEST; /** Constant specifying the center of the container */ public static final String CENTER = BorderLayout.CENTER; /* Default constructor: Program */ /** * This code initializes the program data structures. */ protected Program() { JTFTools.registerApplet(this); appletMode = checkForAppletMode(); shown = false; parameterTable = null; finalizers = new ArrayList(); myTitle = getClass().getName(); myTitle = myTitle.substring(myTitle.lastIndexOf(".") + 1); appletStub = new ProgramAppletStub(this); setAppletStub(appletStub); initContentPane(getContentPane()); setVisible(false); setConsole(createConsole()); myDialog = createDialogIO(); myDialog.setAssociatedConsole(myConsole); myMenuBar = createMenuBar(); myConsole.setMenuBar(myMenuBar); } /* Method: run() */ /** * Specifies the code to be executed as the program runs. * The run method is required for applications that have * a thread of control that runs even in the absence of user actions, * such as a program that uses console interation or that involves * animation. GUI-based programs that operate by setting up an initial * configuration and then wait for user events usually do not specify a * run method and supply a new definition for init * instead. */ public void run() { /* Empty */ } /* Method: init() */ /** * Specifies the code to be executed as startup time before the * run method is called. Subclasses can override this * method to perform any initialization code that would ordinarily * be included in an applet init method. In general, * subclasses will override init in GUI-based programs * where the program simply sets up an initial state and then waits * for events from the user. The run method is required * for applications in which there needs to be some control thread * while the program runs, as in a typical animation. * * Example: program.init(); */ public void init() { /* Empty */ } /* Method: print(value) */ /** * Displays the argument value on the console, leaving the cursor at the end of * the output. The print method is overloaded so that * value can be of any type. * * Example: program.print(value); * @param value The value to be displayed */ public void print(String value) { getOutputModel().print(value); } /** * Makes sure that print can display a boolean. * @param x The value to be displayed */ public final void print(boolean x) { print("" + x); } /** * Makes sure that print can display a char. * @param x The value to be displayed */ public final void print(char x) { print("" + x); } /** * Makes sure that print can display a double. * @param x The value to be displayed */ public final void print(double x) { print("" + x); } /** * Makes sure that print can display a float. * @param x The value to be displayed */ public final void print(float x) { print("" + x); } /** * Makes sure that print can display an int. * @param x The value to be displayed */ public final void print(int x) { print("" + x); } /** * Makes sure that print can display a long. * @param x The value to be displayed */ public final void print(long x) { print("" + x); } /** * Makes sure that print can display an Object. * @param x The value to be displayed */ public final void print(Object x) { print("" + x); } /* Method: println() */ /** * Advances the console cursor to the beginning of the next line. * * Example: program.println(); */ public void println() { getOutputModel().println(); } /* Method: println(value) */ /** * Displays the argument value on the console and then advances the cursor * to the beginning of the next line. The println method is * overloaded so that value can be of any type. * * Example: program.println(value); * @param value The value to be displayed */ public void println(String value) { getOutputModel().println(value); } /** * Makes sure that println can display a boolean. * @param x The value to be displayed */ public final void println(boolean x) { println("" + x); } /** * Makes sure that println can display a char. * @param x The value to be displayed */ public final void println(char x) { println("" + x); } /** * Makes sure that println can display a double. * @param x The value to be displayed */ public final void println(double x) { println("" + x); } /** * Makes sure that println can display a float. * @param x The value to be displayed */ public final void println(float x) { println("" + x); } /** * Makes sure that println can display an int. * @param x The value to be displayed */ public final void println(int x) { println("" + x); } /** * Makes sure that println can display a long. * @param x The value to be displayed */ public final void println(long x) { println("" + x); } /** * Makes sure that println can display an Object. * @param x The value to be displayed */ public final void println(Object x) { println("" + x); } /* Method: showErrorMessage(msg) */ /** * Displays the error message in the standard output model. * * Example: showErrorMessage(msg); * @param msg The error msg to be displayed */ public void showErrorMessage(String msg) { getOutputModel().showErrorMessage(msg); } /* Method: readLine() */ /** * Reads and returns a line of input from the console. The end-of-line * characters that terminate the input are not included in the returned * string. * * Example: String str = program.readLine(); * @return The next line of input as a String */ public final String readLine() { return readLine(null); } /* Method: readLine(prompt) */ /** * Prompts the user for a line of input. The end-of-line characters * that terminate the input are not included in the returned string. * * Example: String str = program.readLine(prompt); * @param prompt The prompt string to display to the user * @return The next line of input as a String */ public String readLine(String prompt) { return getInputModel().readLine(prompt); } /* Method: readInt() */ /** * Reads and returns an integer value from the user. If the user types * a value that is not a legal integer, the method ordinarily offers the * user a chance to reenter the data, although this behavior can be * changed using the * setExceptionOnError method. * * Example: int n = program.readInt(); * @return The value of the input interpreted as a decimal integer */ public final int readInt() { return readInt(null, Integer.MIN_VALUE, Integer.MAX_VALUE); } /* Method: readInt(low, high) */ /** * Reads and returns an integer value from the user, which is constrained to * be within the specified inclusive range. If the user types a value * that is not a legal integer, the method ordinarily offers the user a chance * to reenter the data, although this behavior can be changed using the * setExceptionOnError method. * * Example: int n = program.readInt(low, high); * @param low The lowest value in the permitted range * @param high The highest value in the permitted range * @return The value of the input interpreted as a decimal integer */ public final int readInt(int low, int high) { return readInt(null, low, high); } /* Method: readInt(prompt) */ /** * Prompts the user to enter an integer, which is then returned as the value * of this method. If the user types a value that is not a legal integer, * the method ordinarily offers the user a chance to reenter the data, * although this behavior can be changed using the * setExceptionOnError method. * * Example: int n = program.readInt(prompt); * @param prompt The prompt string to display to the user * @return The value of the input interpreted as a decimal integer */ public final int readInt(String prompt) { return readInt(prompt, Integer.MIN_VALUE, Integer.MAX_VALUE); } /* Method: readInt(prompt, low, high) */ /** * Prompts the user to enter an integer, which is then returned as the value * of this method. The value must be within the inclusive range between * low and high. If the user types a value that * is not a legal integer or is outside the specified range, the method * ordinarily offers the user a chance to reenter the data, * although this behavior can be changed using the * setExceptionOnError method. * * Example: int n = console.readInt(prompt, low, high); * @param prompt The prompt string to display to the user * @param low The lowest value in the permitted range * @param high The highest value in the permitted range * @return The value of the input interpreted as a decimal integer */ public int readInt(String prompt, int low, int high) { return getInputModel().readInt(prompt, low, high); } /* Method: readDouble() */ /** * Reads and returns a double-precision value from the user. If the user * types a value that is not a legal number, the method ordinarily offers * the user a chance to reenter the data, although this behavior can be * changed using the * setExceptionOnError method. * * Example: double d = program.readDouble(); * @return The value of the input interpreted as a double */ public final double readDouble() { return readDouble(null, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); } /* Method: readDouble(low, high) */ /** * Reads and returns a double-precision value from the user, which is * constrained to be within the specified inclusive range. If the user * types a value that is not a legal number, the method ordinarily offers * the user a chance to reenter the data, although this behavior can be * changed using the * setExceptionOnError method. * * Example: double d = program.readDouble(low, high); * @param low The lowest value in the permitted range * @param high The highest value in the permitted range * @return The value of the input interpreted as a double */ public final double readDouble(double low, double high) { return readDouble(null, low, high); } /* Method: readDouble(prompt) */ /** * Prompts the user to enter an double-precision number, which is then * returned as the value of this method. If the user types a value that * is not a legal number, the method ordinarily offers the user a chance to * reenter the data, although this behavior can be changed using the * setExceptionOnError method. * * Example: double d = program.readDouble(prompt); * @param prompt The prompt string to display to the user * @return The value of the input interpreted as a double */ public final double readDouble(String prompt) { return readDouble(prompt, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); } /* Method: readDouble(prompt, low, high) */ /** * Prompts the user to enter an double-precision number, which is then returned * as the value of this method. The value must be within the inclusive range * between low and high. If the user types a value * that is not a legal number, the method ordinarily offers the user a chance * to reenter the data, although this behavior can be changed using the * setExceptionOnError method. * * Example: d = program.readDouble(prompt, low, high); * @param prompt The prompt string to display to the user * @param low The lowest value in the permitted range * @param high The highest value in the permitted range * @return The value of the input interpreted as a double */ public double readDouble(String prompt, double low, double high) { return getInputModel().readDouble(prompt, low, high); } /* Method: readBoolean() */ /** * Reads and returns a boolean value (true or false). * The input must match one of these strings, ignoring case. If the user * types a value that is not one of these possibilities, the method ordinarily * offers the user a chance to reenter the data, although this behavior * can be changed using the * setExceptionOnError method. * * Example: boolean flag = program.readBoolean(); * @return The value of the input interpreted as a boolean value */ public final boolean readBoolean() { return readBoolean(null); } /* Method: readBoolean(prompt) */ /** * Prompts the user to enter a boolean value, which is returned as * the value of this method. If the user types a value that is not a * legal boolean value, the method ordinarily offers the user a chance * to reenter the data, although this behavior can be changed using the * setExceptionOnError method. * * Example: boolean flag = program.readBoolean(prompt); * @param prompt The prompt string to display to the user * @return The value of the input interpreted as a boolean value */ public final boolean readBoolean(String prompt) { return readBoolean(prompt, "true", "false"); } /* Method: readBoolean(prompt, trueLabel, falseLabel) */ /** * Prompts the user to enter a boolean value, which is matched against the * labels provided. If the user enters a value that is not one of the two * choices, readBoolean ordinarily offers the user a chance * to reenter the data, although this behavior can be changed using the * setExceptionOnError method. * * Example: boolean flag = program.readBoolean(prompt); * @param prompt The prompt string to display to the user * @param trueLabel The string used to indicate true * @param falseLabel The string used to indicate false * @return The value of the input interpreted as a boolean value */ public boolean readBoolean(String prompt, String trueLabel, String falseLabel) { return getInputModel().readBoolean(prompt, trueLabel, falseLabel); } /* Method: isAppletMode() */ /** * Returns true if this program is running as an applet in a browser. * * Example: if (isAppletMode()) . . . * @return true if this program is running as an applet, false otherwise */ public boolean isAppletMode() { return appletMode; } /* Method: setConsole(console) */ /** * Sets the console associated with this program. * * Example: program.setConsole(console); * @param console The IOConsole object used for this program */ public void setConsole(IOConsole console) { myConsole = console; } /* Method: getConsole() */ /** * Returns the console associated with this program. * * Example: IOConsole console = program.getConsole(); * @return The IOConsole object used for this program */ public IOConsole getConsole() { return myConsole; } /* Method: setDialog(dialog) */ /** * Sets the dialog associated with this program. * * Example: program.setDialog(dialog); * @param dialog The IODialog object used for this program */ public void setDialog(IODialog dialog) { myDialog = dialog; } /* Method: getDialog() */ /** * Returns the dialog used for user interaction. * * Example: IODialog dialog = program.getDialog(); * @return The IODialog object used for this program */ public IODialog getDialog() { return myDialog; } /* Method: setInputModel(io) */ /** * Sets the input model associated with this program. * * Example: program.setInputModel(io); * @param io The input model used for this program */ public void setInputModel(IOModel io) { inputModel = io; } /* Method: setOutputModel(io) */ /** * Sets the output model associated with this program. * * Example: program.setOutputModel(io); * @param io The IOModel object used as the output model */ public void setOutputModel(IOModel io) { outputModel = io; } /* Method: getInputModel() */ /** * Returns the IOModel used for program input, which will * ordinarily be the console. * * Example: IOModel io = program.getInputModel(); * @return The IOModel used for program input */ public IOModel getInputModel() { return (inputModel == null) ? myConsole : inputModel; } /* Method: getOutputModel() */ /** * Returns the IOModel used for program output, which will * ordinarily be the console. * * Example: IOModel io = program.getOutputModel(); * @return The IOModel used for program output */ public IOModel getOutputModel() { return (outputModel == null) ? myConsole : outputModel; } /* Method: getReader() */ /** * Returns a BufferedReader whose input comes from the console. * * Example: BufferedReader rd = getReader(); * @return A Reader for use with this console */ public BufferedReader getReader() { return getConsole().getReader(); } /* Method: getWriter() */ /** * Returns a PrintWriter whose output is directed to the console. * * Example: PrintWriter wr = getWriter(); * @return A PrintWriter for use with this console */ public PrintWriter getWriter() { return getConsole().getWriter(); } /* Method: getRegionPanel(region) */ /** * Gets the JPanel for the specified region. * * Example: JPanel panel = getRegionPanel(region); * @param region The region of the window (NORTH, SOUTH, * EAST, WEST, or CENTER) * @return The JPanel for that subregion */ public JPanel getRegionPanel(String region) { if (region.equals(NORTH)) { return northPanel; } else if (region.equals(SOUTH)) { return southPanel; } else if (region.equals(WEST)) { return westPanel; } else if (region.equals(EAST)) { return eastPanel; } else if (region.equals(CENTER)) { return centerPanel; } else { throw new ErrorException("getRegionPanel: Illegal region " + region); } } /* Method: add(comp, region, constraints) */ /** * Adds the component to the specified border region with the indicated * constraints object. * * Example: add(comp, region, constraints); * @param comp The component to be added * @param region The region of the window (NORTH, SOUTH, * EAST, WEST, or CENTER) * @param constraints The constraints object */ public void add(Component comp, String region, Object constraints) { if (region.equals(NORTH)) { northPanel.add(comp, constraints); } else if (region.equals(SOUTH)) { southPanel.add(comp, constraints); } else if (region.equals(WEST)) { westPanel.add(comp, constraints); } else if (region.equals(EAST)) { eastPanel.add(comp, constraints); } else if (region.equals(CENTER)) { centerPanel.add(comp, constraints); } else { throw new ErrorException("add: Illegal region " + region); } } /* Method: addActionListeners() */ /** * Adds the program as an ActionListener to every button in * the structure that does not have a listener already. * * Example: addActionListeners(); */ public void addActionListeners() { addActionListeners(this); } /* Method: addActionListeners(listener) */ /** * Adds the specified listener to every button in * the structure that does not have a listener already. * * Example: addActionListeners(listener); * @param listener The ActionListener to be added */ public void addActionListeners(ActionListener listener) { addActionListeners(getContentPane(), listener); } /* Method: setTitle(title) */ /** * Sets the title of this program. The title appears in the title bar * when the program is running as an application. * * Example: setTitle(title); * @param title The title for this program */ public void setTitle(String title) { myTitle = title; if (programFrame != null) programFrame.setTitle(title); } /* Method: getTitle() */ /** * Gets the title of this program. * * Example: String title = getTitle(); * @return The title in use for this program */ public String getTitle() { return myTitle; } /* Method: getMenuBar() */ /** * Returns the menu bar associated with this program. Note that this menu bar * cannot be set by clients, although it can be changed initially by overriding * the createMenuBar factory method. * * Example: ProgramMenuBar mbar = getMenuBar(); * @return The menu bar in use for this program */ public ProgramMenuBar getMenuBar() { return myMenuBar; } /* Method: start(args) */ /** * Starts the program using the specified argument list. * * Example: program.start(args); * @param args An array of strings passed to the program */ public void start(String[] args) { if (parameterTable == null) parameterTable = createParameterTable(args); if (getParent() == null) initApplicationFrame(); validate(); setVisible(true); if (programFrame != null) { programFrame.validate(); int nComponents = centerPanel.getComponentCount(); nComponents += northPanel.getComponentCount(); nComponents += southPanel.getComponentCount(); nComponents += westPanel.getComponentCount(); nComponents += eastPanel.getComponentCount(); if (nComponents > 0) { programFrame.setVisible(true); shown = true; } circumventFrameSizeBug(programFrame, programBounds.getSize()); } started = true; init(); if (programFrame != null && myMenuBar != null) { myMenuBar.install(programFrame); } validate(); startRun(); } /* Method: exit() */ /** * Exits from the program. Subclasses should override this method if they need * to perform any actions before shutting down the program, such as asking the * user to save any unsaved files. Any clients that do override this method * should call super.exit() at the end of their processing. * * Example: program.exit(); */ public void exit() { int nFinalizers = finalizers.size(); for (int i = 0; i < nFinalizers; i++) { Object obj = finalizers.get(i); try { Class c = obj.getClass(); Method exit = c.getMethod("exit", new Class[0]); exit.invoke(obj, new Object[0]); } catch (Exception ex) { throw new ErrorException(ex); } } JTFTools.terminateAppletThreads(this); if (!appletMode) System.exit(0); } /* Method: addExitHook(obj) */ /** * Requests that the program call the exit method in the * specified object before exiting. * * @param obj finalizer * * Example: program.addExitHook(obj); */ public void addExitHook(Object obj) { finalizers.add(obj); } /* Method: setParameter(name, value) */ /** * Sets a new value for the named parameter. * * Example: setParameter(name, value); * @param name The name of the parameter * @param value The new value */ public void setParameter(String name, String value) { if (parameterTable == null) { parameterTable = new HashMap(); } parameterTable.put(name.toLowerCase(), value); } /* Method: getMainThread() */ /** * Returns the thread that is running the main program. * * Example: Thread mainThread = getMainThread(); * @return The thread that is running the main program, if any */ public Thread getMainThread() { return (appletStarter == null) ? null : appletStarter.getMainThread(); } /* Method: pause(milliseconds) */ /** * Delays the calling thread for the specified time, which is expressed in * milliseconds. Unlike Thread.sleep, this method never throws an * exception. * * Example: program.pause(milliseconds); * @param milliseconds The sleep time in milliseconds */ public void pause(double milliseconds) { JTFTools.pause(milliseconds); } /* Method: getCentralRegionSize() */ /** * Returns the size of the central region. If the content pane has * not been validated, this method computes its preferred size by * subtracting the sizes required for the side panels from the size * of the entire frame. * * @return The size of the central region */ public Dimension getCentralRegionSize() { if (centerPanel == null) return super.getSize(); if (initFinished) return centerPanel.getSize(); Dimension size = (programFrame == null) ? super.getSize() : programFrame.getSize(); Insets insets = (programFrame == null) ? super.getInsets() : programFrame.getInsets(); size.width -= westPanel.getPreferredSize().width + eastPanel.getPreferredSize().width; size.width -= insets.left + insets.right; size.height -= northPanel.getPreferredSize().height + southPanel.getPreferredSize().height; size.height -= insets.top + insets.bottom; return size; } /**********************************************************************/ /* Listener methods */ /**********************************************************************/ /* Method: mouseClicked (implements MouseListener) */ /** * Called when the mouse is clicked. A call to mouseClicked * is always preceded by both a mousePressed and a * mouseReleased event for the same source. */ public void mouseClicked(MouseEvent e) { } /* Method: mousePressed (implements MouseListener) */ /** * Called when the mouse button is pressed. */ public void mousePressed(MouseEvent e) { } /* Method: mouseReleased (implements MouseListener) */ /** * Called when the mouse button is released. */ public void mouseReleased(MouseEvent e) { } /* Method: mouseEntered (implements MouseListener) */ /** * Called when the mouse enters the source (which may be * either a component or a GObject). */ public void mouseEntered(MouseEvent e) { } /* Method: mouseExited (implements MouseListener) */ /** * Called when the mouse exits the source (which may be * either a component or a GObject). */ public void mouseExited(MouseEvent e) { } /* Method: mouseMoved (implements MouseMotionListener) */ /** * Called when the mouse is moved. */ public void mouseMoved(MouseEvent e) { } /* Method: mouseDragged (implements MouseMotionListener) */ /** * Called when the mouse is dragged with the button down. Java * makes several guarantees about dragging. First, a * mouseDragged call is always preceded by a * mousePressed call for the same source. If the * mouse is pressed elsewhere and then enters a source with * the button down, no drag event occurs. Moreover, once the * mouse button goes down in a particular source, only that * source will receive mouse events until the button goes up. * Those events, moreover, are reported even in the mouse * travels outside the domain of the object. */ public void mouseDragged(MouseEvent e) { } /* Method: keyTyped (implements KeyListener) */ /** * Called when a key is typed (i.e., pressed and released). */ public void keyTyped(KeyEvent e) { } /* Method: keyPressed (implements KeyListener) */ /** * Called when a key is pressed. */ public void keyPressed(KeyEvent e) { } /* Method: keyReleased (implements KeyListener) */ /** * Called when a key is released. */ public void keyReleased(KeyEvent e) { } /* Method: actionPerformed (implements ActionListener) */ /** * Called when a component (typically a button) is activated. */ public void actionPerformed(ActionEvent e) { } /**********************************************************************/ /* Factory methods */ /**********************************************************************/ /* Factory method: createProgramFrame() */ /** * Creates the frame containing the program. * * Example: Frame frame = program.createProgramFrame(); * @return The newly allocated Frame object */ protected JFrame createProgramFrame() { return new ProgramFrame(getTitle()); } /* Factory method: createConsole() */ /** * Creates the console used by the ConsoleProgram. Subclasses can * override this method to create their own console types. * * Example: IOConsole console = program.createConsole(); * @return The console to be used by the program */ protected IOConsole createConsole() { return IOConsole.SYSTEM_CONSOLE; } /* Factory method: createDialogIO() */ /** * Creates the dialog used for interaction (primarily by the DialogProgram * class). Subclasses can override this method to create their own dialog types. * * Example: IODialog dialog = program.createDialogIO(); * @return The dialog to be used by the program */ protected IODialog createDialogIO() { return new IODialog(getContentPane()); } /* Factory method: createMenuBar() */ /** * Creates a menu bar for use with the program. * * Example: ProgramMenuBar menuBar = createMenuBar(); * @return A menu bar for use with this Program */ protected ProgramMenuBar createMenuBar() { return new ProgramMenuBar(this); } /**********************************************************************/ /* Overrides of existing methods */ /**********************************************************************/ /* Overridden method: getPreferredSize() */ /** * Returns the preferred size of the content pane. * * Example: Dimension size = getPreferredSize(); * @return The preferred size of the content pane */ public Dimension getPreferredSize() { return computeProgramBounds().getSize(); } /* Overridden method: getWidth() */ /** * Returns the width of the central region. * * Example: int width = getWidth(); * @return The width of the central region */ public int getWidth() { String caller = getMyCaller(); if (caller.startsWith("java.") || caller.startsWith("javax.")) { return super.getWidth(); } else { return getCentralRegionSize().width; } } /* Overridden method: getHeight() */ /** * Returns the height of the central region. * * Example: int height = getHeight(); * @return The height of the central region */ public int getHeight() { String caller = getMyCaller(); if (caller.startsWith("java.") || caller.startsWith("javax.")) { return super.getHeight(); } else { return getCentralRegionSize().height; } } /* Overridden method: getParameter(name) */ /** * Returns the parameter associated with name. * * Example: String value = getParameter(name); * @param name The name of the parameter * @return The value associated with the parameter, or null if none */ public String getParameter(String name) { String value = null; if (parameterTable != null) { value = parameterTable.get(name.toLowerCase()); } if (value != null) return value; return super.getParameter(name); } /* Overridden method: setLayout(layout) */ /** * Sets the layout manager for the central region of the content pane. * * Example: setLayout(layout); * @param layout The layout manager to use */ public void setLayout(LayoutManager layout) { if (isRootPaneCheckingEnabled()) { centerPanel.setLayout(layout); } else { super.setLayout(layout); } } /* Overridden method: getLayout() */ /** * Gets the layout manager for the central region of the content pane. * * Example: LayoutManager layout = setLayout(); * @return The active layout manager */ public LayoutManager getLayout() { if (isRootPaneCheckingEnabled()) { return centerPanel.getLayout(); } else { return super.getLayout(); } } /* Overridden method: setBackground(color) */ /** * Sets the background for the central region of the content pane. * * Example: setBackground(color); * @param color The new background color */ public void setBackground(Color color) { if (isRootPaneCheckingEnabled()) { centerPanel.setBackground(color); } super.setBackground(color); } /* Overridden method: addImpl(comp, constraints, index) */ /** * Adds the specified component to the content pane using the specified constraints and index. */ protected void addImpl(Component comp, Object constraints, int index) { if (isRootPaneCheckingEnabled()) { if (constraints == null) { centerPanel.add(comp, index); } else if (constraints.equals(NORTH)) { northPanel.add(comp, index); } else if (constraints.equals(SOUTH)) { southPanel.add(comp, index); } else if (constraints.equals(WEST)) { westPanel.add(comp, index); } else if (constraints.equals(EAST)) { eastPanel.add(comp, index); } else if (constraints.equals(CENTER)) { centerPanel.add(comp, index); } else { centerPanel.add(comp, constraints, index); } if (!shown && programFrame != null) { programFrame.setVisible(true); shown = true; } } else { super.addImpl(comp, constraints, index); } } /* Overridden method: remove(index) */ /** * Removes the component at the specified index from the central region. * * Example: remove(index); * @param index The index position of the component to remove */ public void remove(int index) { if (isRootPaneCheckingEnabled()) { centerPanel.remove(index); } else { super.remove(index); } } /* Overridden method: remove(comp) */ /** * Removes the specified component from the central region. * * Example: remove(comp); * @param comp The component to remove */ public void remove(Component comp) { if (isRootPaneCheckingEnabled()) { centerPanel.remove(comp); } else { super.remove(comp); } } /* Overridden method: removeAll() */ /** * Removes all components from the central region. * * Example: removeAll(); */ public void removeAll() { if (isRootPaneCheckingEnabled()) { centerPanel.removeAll(); } else { super.removeAll(); } } /* Overridden method: validate() */ /** * Forwards validate to the content pane. * * Example: validate(); */ public void validate() { if (isRootPaneCheckingEnabled()) getContentPane().validate(); super.validate(); } /* Overridden method: repaint() */ /** * Forwards repaint to the content pane. * * Example: repaint(); */ public void repaint() { if (isRootPaneCheckingEnabled()) getContentPane().repaint(); super.repaint(); } /* Overridden method: start() */ /** * Starts the program when it is running in application mode. Note that this * overloads the start method in Applet and therefore * will be called as part of applet startup. * * Example: program.start(); */ public final void start() { appletMode = getParent() != null; if (appletMode) { if (!started) { started = true; validate(); setVisible(true); appletStarter = new AppletStarter(this); appletStarter.start(); } } else { start(null); } } /* Overridden method: destroy() */ /** * Called when the program has been told to destroy itself. The code here * stops the main thread and any animators that have been initiated by this * applet. * * Example: program.destroy(); */ public void destroy() { Animator.shutdown(this); if (appletStarter != null) appletStarter.stop(); } /* Static method: main(args) */ /** * Every application must either contain a "Main-Class" entry in its * manifest file or include a main method that looks like this, where * MyClass is the name of the program class: * *

 


 *      public static void main(String[] args) {
 *         new MyClass().start();
 *      }
 * 
* *

 

If the program needs the command line arguments, the args * array can be passed to the start method and then retrieved * using the getArgumentArray method. * * @param args An array of string arguments */ public static void main(String[] args) { HashMap ht = createParameterTable(args); JTFTools.setDebugOptions(ht.get("debug")); String className = ht.get("code"); if (className == null) { className = JTFTools.getMainClass(); } Class mainClass = null; Program program = null; if (className != null) { if (className.endsWith(".class")) { className = className.substring(0, className.length() - 6); } className = className.replace('/', '.'); CommandLineProgram.checkIfHeadless(className); try { mainClass = Class.forName(className); } catch (ClassNotFoundException ex) { /* Empty */ } } if (mainClass != null) { try { Object obj = mainClass.newInstance(); if (obj instanceof Program) { program = (Program) obj; program.setStartupObject(null); } else { className = ht.get("program"); if (className == null) { throw new ErrorException("Main class does not specify a program"); } program = (Program) Class.forName(className).newInstance(); program.setStartupObject(obj); } } catch (IllegalAccessException ex) { /* Empty */ } catch (InstantiationException ex) { /* Empty */ } catch (ClassNotFoundException ex) { /* Empty */ } } if (program == null) throw new ErrorException("Cannot determine the main class."); program.setParameterTable(ht); program.start(); } /* Method: menuAction(e) */ /** * Called whenever a program action is detected in the menu bar. * Subclasses can override this method to extend the set of menu * commands recognized even in the absence of a component with * keyboard focus. * * @param e action event * @return parent return value */ public boolean menuAction(ActionEvent e) { String cmd = e.getActionCommand(); if (cmd.equals("Quit")) { exit(); } else if (cmd.equals("Print")) { Frame frame = JTFTools.getEnclosingFrame(this); if (frame == null) return true; PrintJob pj = frame.getToolkit().getPrintJob(frame, myTitle, null); if (pj == null) return true; Graphics pg = pj.getGraphics(); pg.translate(PRINT_MARGIN, PRINT_MARGIN); frame.printAll(pg); pj.end(); return true; } else if (cmd.equals("Export Applet") || cmd.equals("Submit Project")) { JTFTools.executeExportAction(this, cmd); return true; } return getConsole().menuAction(e); } /**********************************************************************/ /* Protected methods */ /**********************************************************************/ /* Protected method: getBorder(side) */ /** * Returns the component installed as a border on the specified side, which must * be one of the constants from BorderLayout (NORTH, * SOUTH, EAST, WEST). * * Example: getBorder(side, comp); * @param side The side (NORTH, SOUTH, EAST, * or WEST) * @return The component used as a border on the specified side */ protected Component getBorder(String side) { if (side.equals(NORTH)) return northBorder; if (side.equals(SOUTH)) return southBorder; if (side.equals(EAST)) return eastBorder; if (side.equals(WEST)) return westBorder; throw new ErrorException("Illegal border specification - " + side); } /* Protected method: getArgumentArray() */ /** * Retrieves the array of arguments passed in from the command line, or * null if no arguments are available. * * Example: String[] args = getArgumentArray(); * @return The array of command-line arguments */ protected String[] getArgumentArray() { if (parameterTable == null) return null; StringTokenizer tokenizer = new StringTokenizer(parameterTable.get("ARGS"), "\t", false); String[] args = new String[tokenizer.countTokens()]; for (int i = 0; tokenizer.hasMoreTokens(); i++) { args[i] = tokenizer.nextToken(); } return args; } /* Protected method: isStarted() */ /** * Checks to see whether this program has started, usually by checking to see * whether some pane exists. Subclasses can override this method to ensure * that their structures are visible before proceeding. * * @return if the program is already running */ protected boolean isStarted() { IOConsole console = getConsole(); if (console == null) return false; if (console.getParent() == null) return true; Dimension size = console.getSize(); return (console.isShowing()) && (size.width != 0) && (size.height != 0); } /* Protected method: startHook() */ /** * Performs class-specific initialization for the program just before * it starts. */ protected void startHook() { /* Empty */ } /* Protected method: endHook() */ /** * Performs class-specific cleanup for the program just after * it finishes. */ protected void endHook() { /* Empty */ } /* Protected method: setAppletStub(stub) */ /** * Sets the applet stub for this program in a way that makes it possible for * clients to retrieve it. * * Example: setAppletStub(stub); * @param stub The applet stub */ protected void setAppletStub(AppletStub stub) { appletStub = stub; setStub(stub); } /* Protected method: getAppletStub() */ /** * Retrieves the applet stub. * * Example: AppletStub stub = getAppletStub(); * @return The applet stub */ protected AppletStub getAppletStub() { return appletStub; } /* Protected method: setParameterTable(ht) */ /** * Sets the parameter table for this program. * * Example: setParameterTable(ht); * @param ht The parameter table */ protected void setParameterTable(HashMap ht) { parameterTable = ht; } /* Protected method: getParameterTable() */ /** * Retrieves the parameter table. * * Example: ParameterTable ht = getParameterTable(); * @return The parameter table */ protected HashMap getParameterTable() { return parameterTable; } /* Protected method: setStartupObject(obj) */ /** * Sets the object that is created when the program is started so that * it can be retrieved later by getStartupObject. * * Example: setStartupObject(obj); * @param obj The startup object */ protected void setStartupObject(Object obj) { startupObject = obj; } /* Protected method: getStartupObject() */ /** * Retrieves the object that was created when this program was started * if that object is something other than a Program. In * the normal case of running a Program object, this method * will return null. * * Example: Object obj = getStartupObject(); * @return The startup object */ protected Object getStartupObject() { return startupObject; } /* Protected method: startRun() */ /** * Initializes and runs the run method. */ protected void startRun() { ProgramStartupListener listener = new ProgramStartupListener(); Component root = getRootPane(); if (root.isShowing()) { root.addComponentListener(listener); root.validate(); listener.waitForStartup(this); root.update(root.getGraphics()); } root.setCursor(Cursor.getDefaultCursor()); initFinished = true; startHook(); runHook(); endHook(); if (!root.isShowing() && !getContentPane().isShowing()) exit(); } /* Protected method: runHook() */ /** * Calls the run method in the program. Subclasses can override this * method to transfer control somewhere else. */ protected void runHook() { run(); } /* Protected static method: createParameterTable(args) */ /** * Creates a hash table containing the parameters specified in the * argument list. Parameters are taken to be any argument that matches * the template * *

 

name=value * * All other arguments are collected as a tab-separated string and placed * in an entry under the key "ARGS". All named parameters * are converted to lower case to preserve the case-insensitive semantics * of getParameter. * * Example: HashMap ht = createParameterTable(args); * @param args The array of strings passed to the application * @return A HashMap containing the parameter bindings */ protected static HashMap createParameterTable(String[] args) { if (args == null) return null; HashMap ht = new HashMap(); String newArgs = ""; for (int i = 0; i < args.length; i++) { String arg = args[i]; int equals = arg.indexOf('='); if (equals > 0) { String name = arg.substring(0, equals).toLowerCase(); String value = arg.substring(equals + 1); ht.put(name, value); } else { if (newArgs.length() > 0) newArgs += '\t'; newArgs += arg; } } ht.put("ARGS", newArgs); return ht; } /**********************************************************************/ /* Private methods */ /**********************************************************************/ /* Private method: initContentPane(contentPane) */ /** * Initializes the content pane to contain its five regions. */ private void initContentPane(Container contentPane) { contentPane.setLayout(new ProgramContentPaneLayout(this)); northPanel = new JPanel(); southPanel = new JPanel(); eastPanel = new JPanel(); westPanel = new JPanel(); centerPanel = new JPanel(); northPanel.setLayout(new TableLayout(1, 0, 5, 5)); southPanel.setLayout(new TableLayout(1, 0, 5, 5)); westPanel.setLayout(new TableLayout(0, 1, 5, 5)); eastPanel.setLayout(new TableLayout(0, 1, 5, 5)); centerPanel.setLayout(new GridLayout(1, 0)); contentPane.add(northPanel, NORTH); contentPane.add(southPanel, SOUTH); contentPane.add(eastPanel, EAST); contentPane.add(westPanel, WEST); contentPane.add(centerPanel, CENTER); } /* Private method: addActionListeners(comp, listener) */ /** * Recursively adds the specified listener as an ActionListener to * every button in the hierarchy. Reflection is used because there are many * possible classes of button-like objects. */ private void addActionListeners(Component comp, ActionListener listener) { if (isButton(comp)) { if (!hasActionListener(comp)) { try { Class[] types = { Class.forName("java.awt.event.ActionListener") }; Object[] args = { listener }; Method addActionListener = comp.getClass().getMethod("addActionListener", types); addActionListener.invoke(comp, args); } catch (Exception ex) { throw new ErrorException(ex); } } } else if (comp instanceof Container) { Container container = (Container) comp; int nComponents = container.getComponentCount(); for (int i = 0; i < nComponents; i++) { addActionListeners(container.getComponent(i), listener); } } } /* Private method: isButton(comp) */ /** * Determines whether the component is a button. */ private boolean isButton(Component comp) { return (comp instanceof Button || comp instanceof JButton); } /* Private method: hasActionListener(comp) */ /** * Returns true if the component has at least one action listener. The method * returns false if the Java runtime is too old to determine the answer. */ private boolean hasActionListener(Component comp) { try { Method getActionListeners = comp.getClass().getMethod("getActionListeners", new Class[0]); ActionListener[] listeners = (ActionListener[]) getActionListeners.invoke(comp, new Object[0]); return (listeners.length > 0); } catch (Exception ex) { return false; } } /* Private method: initApplicationFrame() */ /** * Creates the program frame and puts the program in it. */ private void initApplicationFrame() { programFrame = createProgramFrame(); ((ProgramAppletStub) appletStub).setFrame(programFrame); Container contents = programFrame.getContentPane(); contents.setLayout(new BorderLayout()); contents.add(getContentPane(), BorderLayout.CENTER); programFrame.addWindowListener(new ProgramWindowListener(this)); programBounds = computeProgramBounds(); Insets insets = programFrame.getInsets(); int frameWidth = programBounds.width + insets.left + insets.right; int frameHeight = programBounds.height + insets.top + insets.bottom; programFrame.setBounds(programBounds.x, programBounds.y, frameWidth, frameHeight); } /* Private method: decodeSizeParameter(name, value, max) */ /** * Decodes a size parameter. * * Example: int size = decodeSizeParameter(name, value, max); * @param name The name of the parameter * @param value The default value if the parameter is unspecified * @param max The maximum value if the parameter is specified as a percentage * @return The integer denoting the size */ private int decodeSizeParameter(String name, int value, int max) { String str = getParameter(name); if (str == null) { try { Class mainClass = getClass(); Field field = mainClass.getField("APPLICATION_" + name); Object obj = field.get(null); if (obj instanceof Integer) return ((Integer) obj).intValue(); if (obj instanceof String) { str = (String) obj; } else { return value; } } catch (Exception ex) { return value; } } if (str.equals("*")) str = "100%"; if (str.endsWith("%")) { int percent = Integer.parseInt(str.substring(0, str.length() - 1)); return (int) Math.round(percent / 100.0 * max); } else { return Integer.parseInt(str); } } /* Private method: computeProgramBounds() */ /** * Sets the bounds for this program as specified in the parameters. */ private Rectangle computeProgramBounds() { Dimension size = Toolkit.getDefaultToolkit().getScreenSize(); int width = decodeSizeParameter("WIDTH", DEFAULT_WIDTH, size.width); int height = decodeSizeParameter("HEIGHT", DEFAULT_HEIGHT, size.height); int x = decodeSizeParameter("X", (width >= size.width) ? 0 : DEFAULT_X, size.width); int y = decodeSizeParameter("Y", (height >= size.height) ? 0 : DEFAULT_Y, size.height); return new Rectangle(x, y, width, height); } /* Private method: checkForAppletMode() */ /** * Determines whether this program has been invoked as an applet by scanning * the execution stack to see if the string "Applet" appears * in any method name up the calling chain. */ private boolean checkForAppletMode() { StackTraceElement[] stack = new Throwable().getStackTrace(); for (int i = 1; i < stack.length; i++) { if (stack[i].getMethodName().indexOf("Applet") >= 0) return true; } return false; } /* Private method: getMyCaller() */ /** * Returns the name of the caller of the method that invoked getMyCaller. */ private String getMyCaller() { StackTraceElement[] stack = new Throwable().getStackTrace(); return stack[2].getClassName() + "." + stack[2].getMethodName(); } /* Private method: circumventFrameSizeBug(frame, size) */ /** * In some versions of the JDK, calling getInsets on a Frame fails to return * the insets correctly until the frame is validated. On those systems, it * is impossible to compute the correct frame size in advance. The workaround * is to check that the size of the content pane is equal to what it was supposed * to be after installing and validating it. If it is, do nothing to avoid another * validation repaint. If not, adjust the size of the frame by however much the * content pane is too small. */ private void circumventFrameSizeBug(Frame frame, Dimension size) { Container contentPane = getContentPane(); Dimension actualSize = contentPane.getSize(); if (size.equals(actualSize) || actualSize.width == 0 || actualSize.height == 0) return; Dimension frameSize = frame.getSize(); frameSize.width += size.width - actualSize.width; frameSize.height += size.height - actualSize.height; frame.setSize(frameSize.width, frameSize.height); frame.validate(); } /* Private constants */ private static final int DEFAULT_X = 16; private static final int DEFAULT_Y = 40; private static final int DEFAULT_WIDTH = 754; private static final int DEFAULT_HEIGHT = 492; private static final int PRINT_MARGIN = 36; /* Private instance variables */ private ArrayList finalizers; private HashMap parameterTable; private JFrame programFrame; private AppletStub appletStub; private String myTitle; private ProgramMenuBar myMenuBar; private Component northBorder; private Component southBorder; private Component eastBorder; private Component westBorder; private JPanel northPanel; private JPanel southPanel; private JPanel eastPanel; private JPanel westPanel; private JPanel centerPanel; private IOConsole myConsole; private IODialog myDialog; private IOModel inputModel; private IOModel outputModel; private Object startupObject; private AppletStarter appletStarter; private Rectangle programBounds; private boolean started; private boolean shown; private boolean initFinished; private boolean appletMode; } /* Package class: AppletStarter */ /** * This class creates a new thread in which to execute the run * method in the context of an applet. */ class AppletStarter implements Runnable { /* Constructor: AppletStarter(program) */ /** * Creates an object responsible for starting the main thread for the program. */ public AppletStarter(Program program) { myProgram = program; } /* Method: start() */ /** * Starts a new thread for the program that will execute * the run method. */ public void start() { try { mainThread = new Thread(this); mainThread.start(); if (JTFTools.testDebugOption("startup")) { System.out.println("Starting main thread using Thread package"); } } catch (SecurityException ex) { if (JTFTools.testDebugOption("startup")) { System.out.println("Starting main thread using Executor because " + ex); } forkUsingExecutor(); } } /* Method: stop() */ /** * Stops the main thread, using whichever strategy is appropriate for * the implementation. */ public void stop() { try { if (executor == null) { Class threadClass = Class.forName("java.lang.Thread"); Method stop = threadClass.getMethod("stop", new Class[0]); stop.invoke(mainThread, new Object[0]); } else { Method shutdownNow = executor.getClass().getMethod("shutdownNow", new Class[0]); shutdownNow.invoke(executor, new Object[0]); } } catch (Exception ex) { /* Empty */ } } /* Method: run() */ /** * Starts the program by calling its startRun method, which will * eventually call run in the program. */ public void run() { myProgram.startRun(); } /* Method: getMainThread() */ /** * Returns the thread that is running the main program. */ public Thread getMainThread() { return mainThread; } /* Private method: forkUsingExecutor */ /** * The AppletRunner in Java 5.0 does not allow nontrusted packages to fork * new threads. This class uses the ScheduledExecutor class in * java.util.concurrent to do the fork so that it comes from an * acceptable place. */ private void forkUsingExecutor() { try { Class scheduledExecutorClass = Class.forName("java.util.concurrent.ScheduledExecutor"); Class[] types1 = { Integer.TYPE }; Object[] args1 = { new Integer(1) }; Constructor constructor = scheduledExecutorClass.getConstructor(types1); executor = constructor.newInstance(args1); Class timeUnitClass = Class.forName("java.util.concurrent.TimeUnit"); Field secondsField = timeUnitClass.getField("SECONDS"); Object seconds = secondsField.get(null); Class[] types2 = { Class.forName("java.lang.Runnable"), Long.TYPE, Class.forName("java.util.concurrent.TimeUnit") }; Object[] args2 = { this, new Long(0), seconds }; Method schedule = executor.getClass().getMethod("schedule", types2); schedule.invoke(executor, args2); } catch (Exception ex) { if (JTFTools.testDebugOption("startup")) { System.out.println("Executor failed because " + ex); } executor = null; mainThread = Thread.currentThread(); myProgram.startRun(); } } /* Private instance variables */ private Program myProgram; private Thread mainThread; private Object executor; } /* Package class: ProgramActionListener */ /** * This class listens for global action events in the menu bar. These events are * passed back to the program through the protected globalMenuAction * method. */ class ProgramActionListener implements ActionListener { public ProgramActionListener(Program owner) { program = owner; } /* ActionListener interface */ public void actionPerformed(ActionEvent e) { program.menuAction(e); } /* Private instance variables */ private Program program; } /* Package class: ProgramWindowListener */ /** * This class implements a simple window listener for programs whose only * function is to exit if the window is closed. */ class ProgramWindowListener implements WindowListener { public ProgramWindowListener(Program owner) { program = owner; } /* WindowListener interface */ public void windowClosing(WindowEvent e) { ((Component) e.getSource()).setVisible(false); program.exit(); } public void windowOpened(WindowEvent e) { } public void windowClosed(WindowEvent e) { } public void windowIconified(WindowEvent e) { } public void windowDeiconified(WindowEvent e) { } public void windowActivated(WindowEvent e) { } public void windowDeactivated(WindowEvent e) { } /* Private instance variables */ private Program program; } /* Package class: ProgramStartupListener */ /** * This class implements a component listener that supports the program * startup logic. */ class ProgramStartupListener implements ComponentListener { /* Method: waitForStartup(program) */ /** * Waits until the specified program has started. */ public synchronized void waitForStartup(Program program) { JTFTools.pause(STARTUP_DELAY); while (!program.isStarted()) { try { wait(STARTUP_CYCLE); } catch (InterruptedException ex) { /* Empty */ } } } /* ComponentListener interface */ public void componentHidden(ComponentEvent e) { } public void componentMoved(ComponentEvent e) { } public void componentResized(ComponentEvent e) { componentShown(e); } public synchronized void componentShown(ComponentEvent e) { notifyAll(); } /* Private constants */ private static final int STARTUP_DELAY = 1000; private static final int STARTUP_CYCLE = 300; } /* Package class: ProgramFrame */ /** * This frame represents the visible component that encloses the program. */ class ProgramFrame extends JFrame { public ProgramFrame(String title) { super(title); } public void update(Graphics g) { paint(g); } } /* Package class: ProgramContentPaneLayout */ /** * This layout manager is identical to the BorderLayout manager except that * it also resizes the Program object itself to correspond to the available * space. This class is required for JDK 1.6 systems, when it became * necessary to separate the content pane of the JApplet from the program's * JApplet itself. */ class ProgramContentPaneLayout extends BorderLayout { public ProgramContentPaneLayout(Program program) { myProgram = program; } public void layoutContainer(Container parent) { super.layoutContainer(parent); if (!myProgram.isAncestorOf(parent)) { Dimension psize = parent.getSize(); Insets insets = parent.getInsets(); int x = insets.left; int y = insets.top; int width = psize.width - insets.left - insets.right; int height = psize.height - insets.top - insets.bottom; myProgram.setBounds(x, y, width, height); ComponentEvent e = new ComponentEvent(myProgram, ComponentEvent.COMPONENT_RESIZED); Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(e); } } /* Private instance variables */ private Program myProgram; } /* Package class: ProgramAppletStub */ /** * This class implements both the AppletStub and AppletContext interfaces and allows * standard applications to run with the same capabilities as an applet. */ class ProgramAppletStub implements AppletContext, AppletStub { public ProgramAppletStub(Program program) { applet = program; } public void setFrame(Frame frame) { enclosure = frame; } public boolean isActive() { return true; } public URL getDocumentBase() { return getCodeBase(); } public URL getCodeBase() { try { return new URL("file:" + getCanonicalPath(".")); } catch (MalformedURLException ex) { throw new ErrorException("Error: Illegal document base URL"); } } public String getParameter(String name) { return null; } public AppletContext getAppletContext() { return this; } public void appletResize(int width, int height) { if (enclosure == null) { if (!recursiveResizeCheck) { recursiveResizeCheck = true; applet.resize(width, height); applet.validate(); recursiveResizeCheck = false; } } else { enclosure.setSize(width, height); enclosure.validate(); } } public AudioClip getAudioClip(URL url) { AudioClip clip = null; if (clip == null) clip = getNewAudioClip(url); return clip; } public Image getImage(URL url) { try { Object content = url.getContent(); if (content instanceof ImageProducer) { return applet.createImage((ImageProducer) content); } } catch (IOException ex) { /* Ignore the exception and fail */ } return null; } public Applet getApplet(String name) { return null; } public Enumeration getApplets() { return new Vector().elements(); } public void showDocument(URL url) { if (applet != null) applet.getAppletContext().showDocument(url); } public void showDocument(URL url, String target) { if (applet != null) applet.getAppletContext().showDocument(url, target); } public void showStatus(String status) { if (applet == null) { System.out.println(status); } else { applet.showStatus(status); } } public void setStream(String key, InputStream stream) { throw new ErrorException("setStream: unimplemented operation"); } public InputStream getStream(String key) { throw new ErrorException("getStream: unimplemented operation"); } public java.util.Iterator getStreamKeys() { throw new ErrorException("getStreamKeys: unimplemented operation"); } private String getCanonicalPath(String start) { String path = new File(start).getAbsolutePath(); int sp; while ((sp = path.indexOf(' ')) != -1) { path = path.substring(0, sp) + "%20" + path.substring(sp + 1); } return path; } private synchronized AudioClip getNewAudioClip(URL url) { try { Class type = Class.forName("java.applet.Applet"); Class[] types = { Class.forName("java.net.URL") }; Object[] args = { url }; Method fn = type.getMethod("newAudioClip", types); return (AudioClip) fn.invoke(null, args); } catch (Exception ex) { return null; } } /* Private instance variables */ private Applet applet; private Frame enclosure; private boolean recursiveResizeCheck; } /* Package class: DefaultActionListener */ /** * This class is the default action listener added to buttons. */ class DefaultActionListener implements ActionListener { public DefaultActionListener() { /* Empty */ } public void actionPerformed(ActionEvent e) { Component comp = (Component) e.getSource(); Program program = findProgram(comp); if (program != null && countActionListeners(comp) > 1) { try { Class[] types = { Class.forName("java.awt.event.ActionListener") }; Object[] args = { this }; Method removeActionListener = comp.getClass().getMethod("removeActionListener", types); removeActionListener.invoke(comp, args); return; } catch (Exception ex) { throw new ErrorException(ex); } } String message = "No ActionListener is attached"; if (comp instanceof Button) { message += " to button " + ((Button) comp).getLabel(); } else { try { Method getText = comp.getClass().getMethod("getText", new Class[0]); message += " to button " + (String) getText.invoke(comp, new Object[0]); } catch (Exception ex) { throw new ErrorException(ex); } } if (program == null) { throw new ErrorException(message); } else { program.getDialog().showErrorMessage(message); } } protected static int countActionListeners(Component comp) { try { Method getActionListeners = comp.getClass().getMethod("getActionListeners", new Class[0]); ActionListener[] listeners = (ActionListener[]) getActionListeners.invoke(comp, new Object[0]); return listeners.length; } catch (Exception ex) { return -1; } } private Program findProgram(Component comp) { if (comp instanceof Program) { return (Program) comp; } else if (comp != null) { return findProgram(comp.getParent()); } else { return null; } } }