// // SimObject.java // import java.util.Random; import java.util.Enumeration; import java.util.Vector; import java.awt.Color; abstract public class SimObject implements Runnable { // OVERVIEW: A SimObject is an object than represents a simulator object. // It has a grid (a circular reference to the grid containing this object), // location (integer row and column), initialization state (boolean // that is true if it has beein initialized), and thread pause state (boolean that // is true if the thread is paused). // A typical SimObject is < grid, row, col, initialized, paused >. // // Note: SimObject is an abstract class. The executeTurn method must // be implemented by subclasses. // Rep: protected Grid grid; // subclasses have access to mrow and mcol protected int mrow; protected int mcol; boolean dead = false; //@ghost public boolean isInitialized; private boolean isPaused; /*@non_null@*/ private Random random; // Used to get random numbers. // AF(c) = < grid, mrow, mcol, isInitialized, isPaused > // RI(c) = // if isIntialized then // grid != null // && grid.validLocation (mrow, mcol) // && grid.getObjectAt (mrow, mcol) == this //@invariant isInitialized ==> grid != null // If isInitialized is true, the grid is not null. static final int TURN_DELAY = 500; static final int TURN_RANDOM = 200; public SimObject() // EFFECTS: Creates a new, uninitalized SimObject. //@ensures !isInitialized { random = new Random(); } final public void init(int row, int col, /*@non_null@*/ Grid grid) // REQUIRES: init has not previously been called for this. The cell is at (row, col) on grid. // MODIFIES: this // EFFECTS: Initializes the cell at row, col on grid. // NOTE: This method is final, that means subtypes cannot override this method. //@requires !isInitialized; //@ensures isInitialized; { this.mrow = row; this.mcol = col; this.grid = grid; this.isPaused = false; //@set isInitialized = true; } public void run() // REQUIRES: this has been initialized //@also_requires isInitialized // We use also_requires instead of requires, because we are adding a precondition // to an inherited method. This violates the substitution principle --- subtypes // should make preconditions weaker, not stronger. // EFFECTS: The object thread. If the object is not paused, executes one turn by calling // the executeTurn method, and sleeps for a time and repeats. If the object is paused, // does nothing (until the object is unpaused). { isPaused = false; while (true) { if (!isPaused) { executeTurn(); delay(TURN_DELAY + random.nextInt(TURN_RANDOM)); } } } public void resumeObject() // EFFECTS: Sets paused to false. { isPaused = false; } public void pauseObject() // EFFECTS: Sets paused to true. { isPaused = true; } public boolean isPaused() // EFFECTS: Returns the value of paused. { return isPaused; } abstract public void executeTurn() //@requires isInitialized ; // EFFECTS: Executes one turn for this object. public /*@non_null@*/ Color getColor() // EFFECTS: Returns the color that represents this SimObject, for // painting the grid. { return Color.red; } //@nowarn NonNullResult // ESC/Java spec doesn't know that Color.green is not null. final public int getRow() // REQUIRES: init has previously been called for this. // EFFECTS: Returns the row number that this cell is located in. //@requires isInitialized; { return mrow; } final public void delay(int ms) // EFFECTS: Stalls for ms milliseconds. { try { Thread.sleep(ms); } catch (InterruptedException ie) { ; } } final public int getColumn() // REQUIRES: this is initialized. // EFFECTS: Returns the column number that this cell is located in. //@requires isInitialized; { return mcol; } public void die() { dead = true; } //@ensures \result.elementType == \type(SimObject) synchronized public /*@non_null@*/ Enumeration getNeighbors() { // REQUIRES: The grid must be locked as long as the result of getNeighbors is used. // Otherwise, the neighbors could change if another object moves! // EFFECTS: Returns an Enumeration that yields all the objects adjacent to this // in the grid. Vector objects = new Vector(); //@set objects.elementType = \type(SimObject) //@set objects.containsNull = false for (int row = mrow - 1; row <= mrow + 1; row++) { for (int col = mcol - 1; col <= mcol + 1; col++) { if (row != mrow || col != mcol) { // don't count yourself if (grid.validLocation(row, col)) { SimObject obj = grid.getObjectAt(row, col); if (obj != null) { objects.addElement(obj); } } } } } return objects.elements(); } final public /*@non_null@*/ Grid getGrid() // REQUIRES: init has previously been called for this. // EFFECTS: Returns the grid containing this cell. // NOTE: The rep is exposed. The Grid associated with a SimObject is part of the data abstraction. //@requires isInitialized == true; { return grid; } }