import java.util.Vector; import java.util.Enumeration; public class Grid { // OVERVIEW: A Grid is a 2-dimensional grid. Each sqaure in the grid is either // empty or contains a SimObject object. // // A typical Grid is: [ [ s_0_0, s_0_1, ..., s_0_columns - 1 ], // [ s_1_0, s_1_1, ..., s_1_columns - 1 ], // ... , // [ s_row-1_0, s_row-1_1, ..., s_row-1_columns - 1 ] ] // // Where each s_i_j is either a SimObject object or empty. // /*@spec_public@*/ private int rows; //@invariant rows > 0; /*@spec_public@*/ private int columns; //@invariant columns > 0; // Rep Exposure warning: the objects in simobjects are exposed. /*@monitored@*/ private Vector simobjects; // a vector of objects of type SimObject // Rep Invariant: //@invariant simobjects != null //@invariant simobjects.elementType == \type(SimObject) //@invariant simobjects.containsNull == false // invariant for each s: SimObject in simobjects // s.isInitialized // s.getRow () >= 0 && s.getRow () < rows // s.getCol () >= 0 && s.getCol () < columns // no other t: SimObject in simobjects the same row and column // Abstraction Function: // AF(c) = [ [ s_0_0, s_0_1, ..., s_0_columns - 1 ], // [ s_1_0, s_1_1, ..., s_1_columns - 1 ], // ... , // [ s_row-1_0, s_row-1_1, ..., s_row-1_columns - 1 ] ] // where s_i_j = empty if there is no s: SimObject in simobject with s.getRow () == i and s.getCol () == j // s_i_j = simobjects[k] if simobjects[k].getRow () == i and simobjects[k].getCol () == j Grid (int p_rows, int p_columns) //@requires p_rows > 0; //@requires p_columns > 0; // REQUIRES: p_rows and p_columns must be > 0 // EFFECTS: Constructs a new grid with specified number of rows and // columns. { rows = p_rows; columns = p_columns; simobjects = new Vector(); //@set simobjects.elementType = \type(SimObject) //@set simobjects.containsNull = false } public int numRows() // EFFECTS: Returns the number of rows in the grid. //@ensures \result == rows; { return rows; } public int numColumns() // EFFECTS: Returns the number of columns in the grid. //@ensures \result == columns; { return columns; } synchronized public void startObjects() // EFFECTS: Start all object threads. Previously started // threads will be resumed. { Enumeration els = simobjects.elements (); while (els.hasMoreElements ()) { SimObject current = (SimObject) els.nextElement (); if (current.isPaused ()) { current.resumeObject (); } else { Thread simObjectThread = new Thread (current); simObjectThread.start(); } } } synchronized public void pauseObjects () // EFFECTS: Pause all object threads. { Enumeration els = simobjects.elements (); while (els.hasMoreElements ()) { SimObject current = (SimObject) els.nextElement (); current.pauseObject(); } } public void killObjectAt(int row, int col) { //Requires: Class represented by Object class must be of parent type SimObject simobjects.remove(getObjectAt(row,col)); } synchronized public void setObjectAt (int row, int col, Class objectClass) // REQUIRES: The class represented by objectClass must be of // parent type SimObject. { if (objectClass != null) { try { SimObject newObject = (SimObject) objectClass.newInstance(); //@nowarn Cast; //@assume newObject != null; //@assume !newObject.isInitialized; // ESC/Java doesn't know about newInstance newObject.init (row, col, this); // If an object already exists in the grid with the specified // row and column, remove it before adding the new one. simobjects.remove(getObjectAt(row, col)); simobjects.add(newObject); } catch (java.lang.InstantiationException e) { e.printStackTrace(); } catch (java.lang.IllegalAccessException e) { e.printStackTrace(); } } } synchronized public void setObjectAt(int row, int col, SimObject newObject) throws BadLocationException //@requires newObject != null //@requires row >= 0 && row < xsize //@requires col >= 0 && col < ysize // EFFECTS: places newObject on the grid at location (row,col) (replacing any // object currently on the grid at that location). { //@nowarn Deadlock if (validLocation (row, col)) { newObject.init (row, col, this); // If an object already exists in the grid with the specified // row and column, remove it before adding the new one. simobjects.remove(getObjectAt(row, col)); simobjects.add (newObject); //simobjects[x][y] = newObject; //@nowarn ArrayStore } else { throw new BadLocationException ("" + row + ", " + col); } } //@nowarn Exception synchronized public void setObjectAt(int row, int col, StationaryObstacle newObject) throws BadLocationException //@requires newObject != null //@requires row >= 0 && row < xsize //@requires col >= 0 && col < ysize // EFFECTS: places newObject on the grid at location (row,col) (replacing any // object currently on the grid at that location). { //@nowarn Deadlock if (validLocation (row, col)) { newObject.init (row, col, this); // If an object already exists in the grid with the specified // row and column, remove it before adding the new one. simobjects.remove(getObjectAt(row, col)); simobjects.add (newObject); //simobjects[x][y] = newObject; //@nowarn ArrayStore } else { throw new BadLocationException ("" + row + ", " + col); } } //@nowarn Exception synchronized public boolean isSquareEmpty (int row, int col) throws RuntimeException // REQUIRES: 0 >= row < rows && 0 >= col < columns // EFFECTS: Returns the Cell at the specified row and column. // Returns null if nothing is present there. { return (getObjectAt (row, col) == null); } public boolean validLocation (int row, int col) // EFFECTS: Determines if row, col is a valid grid location. { if((row < 0) || (row > numRows() - 1)) { return false; } if((col < 0) || (col > numColumns() - 1)) { return false; } return true; } // We need the unsynchronized version of getObjectAt for the display --- we want to // be able to display the grid without acquiring a lock. This runs the risk that // the grid will be temporarily displayed in an inconsistent state. /* unsynchronized*/ public SimObject grabObjectAt (int row, int col) throws RuntimeException // REQUIRES: 0 >= row < rows && 0 >= col < columns // EFFECTS: Returns the Cell at the specified row and column. // Returns null if nothing is present there. { if ((row < 0) || (row > numRows () - 1)) { throw new RuntimeException ("Bad row parameter to getObjectAt: " + row); } if ((col < 0) || (col > numColumns () - 1)) { throw new RuntimeException ("Bad column parameter to getObjectAt: " + col); } Enumeration els = simobjects.elements (); //@nowarn Race // Allow the race condition, but be careful using grab. while (els.hasMoreElements ()) { SimObject current = (SimObject) els.nextElement (); //@assume current.isInitialized // couldn't specify this invariant in ESC/Java if ((current.getRow() == row) && (current.getColumn() == col)) { return current; } } return null; } //this function getObjectAt will lock the grid before calling //grabObjectAt so that it is guaranteed that there is no other //object in a certain location. Especially helpful for the //executeTurn function. synchronized public SimObject getObjectAt (int row, int col) throws RuntimeException // REQUIRES: 0 >= row < rows && 0 >= col < columns // EFFECTS: Returns the Cell at the specified row and column. // Returns null if nothing is present there. { return grabObjectAt (row, col); } }