//
// pl-a-2.java
//
// PL Assignment #2
// by Kim, Ki-Yong, 93208-002,
// Dept. of Geography, SNU
// scgyong@nownuri.net
//
import java.util.Vector;
import java.util.Random;
import java.applet.*;
import java.awt.*;
import java.net.*;
/**
* IPBuilding is an Interface from Person
* to Building. A Person has a reference to
* Bulding object that created it, but the reference is
* only needed in the two cases: when calling the elevator,
* and when telling it has finished its job and should
* be deleted. Using IPBuilding rather than Building
* hides much information of Building 's from the Person.
*/
abstract interface IPBuilding {
abstract public void callElevator(Person person,int floorOn);
abstract public void deletePerson(Person person);
}
/**
* IPElevator is an Interface from Person
* to Elevator.
* @see IPBuilding.
*/
abstract interface IPElevator {
abstract public void pushButton(int floorAt);
}
/**
* IEBuilding is an Interface from Elevator
* to Building.
* @see IPBuilding.
*/
abstract interface IEBuilding {
abstract public Floor getFloor(int floor);
}
/**
* Describes Floor class, which contains
* a button and players vector
*/
class Floor {
/*===================
Constants Definition
====================
*/
/**
* Defines topfloor number
*/
static final int TOPFLOOR = 7;
/**
* Defines basefloor number
*/
static final int BASEFLOOR = 1;
/**
* Defines the number of floors
*/
static final int FLOORS = 7;
/*=================
Fields Declaration
==================
*/
/**
* Declares the vector of persons who wait on the floor
*/
protected Vector persons;
/**
* Declares the button state. true if there
* is someone on this floor waiting the elevator
*/
protected boolean buttonOn;
/*=====================
Constructor Definition
======================
*/
/**
* Just does nothing but setting instance variables to null and false
*/
public Floor() {
persons = null;
buttonOn = false;
}
/*=======================
Public Method Definition
========================
*/
/**
* Floor number beginns with 1
* while the java array index with 0.
* Converts given floor number to java array index.
* Just substracts BASEFLOOR.
* Codes had better use floors[Floor.toIndex(i)]
* rather than floors[i-1].
*/
public static final int toIndex(int floor) {
return floor - BASEFLOOR;
}
/**
* Converts given java array index to a floor number.
* Just adds BASEFLOOR.
* @see toIndex(int floor).
*/
public static final int toFloor(int index) {
return index + BASEFLOOR;
}
/**
* Adds given person to waiting persons vector.
* and turn the button on. This method is called
* by Person class.
*/
public synchronized void addPerson(Person person) {
if (persons == null)
persons = new Vector(Building.DEFPERSONS);
persons.addElement(person);
buttonOn = true;
}
/**
* Cuts waiting persons vector and returns it to the caller,
* Elevator
*/
public synchronized Vector getPersons() {
Vector ret = persons;
persons = null;
buttonOn = false;
return ret;
}
/**
* Returns the state of the button. Called by Elevator
*/
public final synchronized boolean isButtonOn() {
return buttonOn;
}
/*=========================
toString Method Definition
==========================
*/
/**
* Returns a string describing the state of this floor
*/
public synchronized String toString() {
if (persons == null)
return "F0;.";
StringBuffer sbuf = new StringBuffer();
int i = 0;
int nPersons = persons.size();
sbuf.append(buttonOn ? "T" : "F");
sbuf.append(nPersons).append(";");
for (i = 0; i < nPersons; i++) {
sbuf.append(persons.elementAt(i).toString());
if (i < nPersons - 1)
sbuf.append(",");
}
sbuf.append(".");
return sbuf.toString();
}
}
/**
* Describes Direction class
* containing constants for elevator direction
*/
class Direction {
/*===================
Constants Definition
====================
*/
/**
* Elevator is not moving, nor visiting. It has stopped.
*/
static final int NONE = 0;
/**
* Upward direction
*/
static final int UPWARD = 1;
/**
* Downward direction
*/
static final int DOWNWARD = -1;
}
/**
* Describes Person class, which plays the roll
* of a passenger of the elevator, as a individual thread.
*/
class Person extends Thread {
/*=================
Fields Declaration
==================
*/
/**
* Interface references to Building and Elevator.
*/
protected IPBuilding building;
protected IPElevator elevator;
/**
* Initial state of a Person.
*/
protected int millisArrival;
protected int floorToGetOn;
protected int floorToGetOff;
/*=====================
Constructor Definition
======================
*/
/**
* Sets arrival time in milliseconds,
* floor to get on, and the floor to get off
*/
public Person(Building b, int arrival, int floorOn, int floorOff) {
building = b;
millisArrival = arrival;
floorToGetOn = floorOn;
floorToGetOff = floorOff;
}
/**
* Main body of the Person thread.
* Sleeps for millisArrival milliseconds, Calls the
* elevator, and then suspends its excution.
* When elevator comes and wakes this persons, it gets on the
* elevator, Pushes the button of the floor to get off at,
* and then suspends once more.
* Elevator wakes once more when it comes to the floor the
* person is to get off at. This is done by Elevator 's
* call to Person.getOff(). And then, the person
* leaves the building.
*/
public void run() {
// this person not yet come to the elevator
elevator = null;
try {
sleep(millisArrival);
} catch (InterruptedException e) {
return;
}
// just came to the elevator.
// call the elevator and wait for
// the elevator to come
building.callElevator(this, floorToGetOn);
suspend();
// got on the elevator.
// choose the button of the floor to get off
elevator.pushButton(floorToGetOff);
// and then wait until the person got off the elevator
suspend();
// go away for his or her own business
building.deletePerson(this);
}
/**
* Checks if given floor is the one this person is to get off at.
* This method is called by Elevator.letGetOff() in the
* Elevator 's thread context. The person thread is
* still suspended.
*/
public boolean isGettingOff(int floor) {
return floor == floorToGetOff ? true : false;
}
/**
* Lets this person to get on the given Elevaotr elev.
* This method is called by Elevator.letGetOn() in the
* Elevator 's thread context. By this call, the
* person thread resumes its execution, which will pushes
* the button with the call elevator.pushButton().
*/
public void getOn(Elevator elev) {
elevator = elev;
resume();
}
/**
* When elevator calls Person.isGettingOff() and gets
* true as a return value, it then lets
* the person get off by calling this method in its
* thread context. Then, the person thread resumes.
*/
public void getOff() {
elevator = null;
resume();
}
/*=========================
toString Method Definition
==========================
*/
/**
* Returns a string describing the state of this person
*/
public String toString() {
StringBuffer sbuf = new StringBuffer();
sbuf.append("P").append(floorToGetOn);
sbuf.append(":").append(floorToGetOff);
sbuf.append("(").append(millisArrival).append(")");
return sbuf.toString();
}
}
/**
* Describes Elevator class.
*
And it also has a reference to
* Building in which it is built.
*/
class Elevator extends Thread implements IPElevator {
/*===================
Constants Definition
====================
*/
/**
* Defines the default speed of the elevator
*/
static final int DEFMOVINGTIME = 500;
static final int DEFSTAYINGTIME = 500;
/*=================
Fields Declaration
==================
*/
/**
* Persons on the elevator
*/
protected Vector persons;
/**
* Flags of whether it should stop at
*/
protected boolean[] doesStopAt;
/**
* Moving state
*/
protected int floorCur;
protected int direction;
protected boolean moving;
/**
* Reference to Building
*/
protected IEBuilding building;
/**
* Speed of the elevator
*/
protected int nStayingTime;
protected int nMovingTime;
/**
* Implementational use
*/
protected boolean loop;
/*=====================
Constructor Definition
======================
*/
/**
* Initializes the elevator. Creates persons vector and flags array.
*/
public Elevator(Building b) {
building = b;
persons = new Vector(Building.DEFPERSONS);
doesStopAt = new boolean[Floor.FLOORS];
floorCur = Floor.BASEFLOOR;
direction = Direction.NONE;
moving = false;
loop = true;
nStayingTime = DEFSTAYINGTIME;
nMovingTime = DEFMOVINGTIME;
}
/*=======================
Public Method Definition
========================
*/
/**
* Sets the speed of the elevator. This method is called by
* the Elevator right after it creates this elevator,
* if overriding the speed is needed.
*/
public void setSpeed(int stay, int move) {
nStayingTime = stay;
nMovingTime = move;
}
/**
* Ends the thread setting the boolean variable
* loop to false. This method is called
* by Building when there is no person in that building
* to come to use the elevator. while Person
* threads the Building creates die themselves, the
* Elevator thread would permanantly survive if not being
* stopped. In addition, this is called by finalize
* method of Buildig.
*/
public synchronized void close() {
boolean suspended = loop;
loop = false;
if (suspended)
resume();
}
/**
* Turns on the button in the elevator. Once the elevator
* wakes a person waiting on each floor, He/She
* will call this method in his/her thread context.
*/
public synchronized void pushButton(int floorAt) {
doesStopAt[Floor.toIndex(floorAt)] = true;
resume();
}
/**
* Starts the thread. Thread 's method run()
* is overrided to start executing the Elevator 's own code.
* This method won't return until close(),
* turning loop off, is called.
*/
public void run() {
while (loop) {
if (!hasSomethingToDo()) {
suspend();
/*
* resume() may be called by close(). in this case,
* loop is already false, so exit the while loop.
* Of course, in many cases, resume() is called by
* Building's callElevator() method, in the
* Thread's context.
*/
continue;
}
/*
* checks if the elevator has to stop at floorCur floor
*/
if (someoneToGetOff() || someoneToGetOn()) {
/*
* uses time to stop the elevator
*/
try {
sleep(nStayingTime);
} catch (InterruptedException e) {
return;
}
/*
* See the comment above the method letGetOn()
*/
if (someoneToGetOff())
letGetOff();
if (someoneToGetOn())
letGetOn();
}
/*
* returning true of setDirection()
* means direction is either
* Direction.UPWARD or Direction.DOWNWARD
*/
boolean hasToMove = setDirection();
if (hasToMove) {
try {
sleep(nMovingTime);
} catch (InterruptedException e) {
return;
}
/*
* moves the elevator
*/
move();
}
}
}
/*=========================
toString Method Definition
==========================
*/
/**
* Returns a string describing the state of the elevator
*/
public synchronized String toString() {
StringBuffer sbuf = new StringBuffer();
sbuf.append(floorCur).append("|");
sbuf.append(direction == Direction.NONE ? "-" :
direction == Direction.UPWARD ? "^" : "v");
sbuf.append(moving ? "M" : "S");
for (int i = 0; i < doesStopAt.length; i++) {
sbuf.append(doesStopAt[i] ? "*" : "-");
}
sbuf.append(persons.size()).append(";");
sbuf.append(persons).append(".");
return sbuf.toString();
}
/*==========================
protected Method Definition
===========================
*/
/**
* Returns true if there is anyone who is
* to get off the elevator on the current floor.
* @see letGetOn()
*/
protected synchronized final boolean someoneToGetOff() {
return doesStopAt[Floor.toIndex(floorCur)];
}
/**
* Lets persons get off the elevator on the current floor.
* @see letGetOn()
*/
protected synchronized void letGetOff() {
for (int i = 0; i < persons.size(); i++) {
Person person = (Person)persons.elementAt(i);
if (person.isGettingOff(floorCur)) {
person.getOff();
persons.removeElement(person);
i--;
}
}
doesStopAt[Floor.toIndex(floorCur)] = false;
}
/**
* Returns true if there is anyone who is
* waiting to get on the elevator on the current floor.
* @see letGetOn()
*/
protected synchronized final boolean someoneToGetOn() {
return building.getFloor(floorCur).isButtonOn();
}
/**
* Lets persons get off the elevator on the current floor.
* * Getting off and getting on are not synchronized * into one synchronized method. * This is because when persons are getting off, * other person may come to that floor. * In addition, checking get-on flag and letting get-on * are also not synchronized. If there is no one to * get on, this is simple; there is no need to let * get on. But, When the elevator sees the get-on * flag is true and decides to stop, * the person who came while the elevator checked * the get-on flag, should be able to have a chance to * get on the elevator. This is why not the entire code blocks, * but the separate four methods are * synchronized. */ protected synchronized void letGetOn() { Vector persons = building.getFloor(floorCur).getPersons(); if (persons == null) return; while (!persons.isEmpty()) { Person person = (Person)persons.firstElement(); this.persons.addElement(person); person.getOn(this); persons.removeElement(person); } } /** * Returns true if the elevator is to * stop at the floors between from and to. * The elevator askes this to the each floor. */ protected synchronized boolean hasToStopBetween(int from, int to) { for (int i = from; i <= to; i++) { if (doesStopAt[Floor.toIndex(i)]) return true; if (building.getFloor(i).isButtonOn()) return true; } return false; } /** * Returns true if there is someone in the elevator, * or someone is waiting for the elevator at any floor. */ protected boolean hasSomethingToDo() { if (!persons.isEmpty()) return true; if (hasToStopBetween(Floor.BASEFLOOR, Floor.TOPFLOOR)) return true; return false; } /** * Sets the next direction of the elevator, and if it is * not a Direction.NONE, sets the moving flag * to true and returns the moving. */ protected synchronized boolean setDirection() { boolean downWard = hasToStopBetween(Floor.BASEFLOOR, floorCur - 1); boolean upWard = hasToStopBetween(floorCur + 1, Floor.TOPFLOOR); if (direction == Direction.UPWARD || direction == Direction.NONE) { if (upWard) { direction = Direction.UPWARD; } else if (downWard) { direction = Direction.DOWNWARD; } else { direction = Direction.NONE; } } else if (direction == Direction.DOWNWARD) { if (downWard) { ; } else if (upWard) { direction = Direction.UPWARD; } else { direction = Direction.NONE; } } if (direction == Direction.NONE) { return false; } return moving = true; } /** * Moves the elevator and set the movingflag off */ protected synchronized void move() { if (direction == Direction.UPWARD) { floorCur++; } else if (direction == Direction.DOWNWARD) { floorCur--; } moving = false; } } /** * Describes Building class. *