// Distributed sort animation // // Mika Katara & Risto Pitkänen 1996 // // $Id: DisCoApplet.java,v 1.1 1996/05/09 07:55:08 rike Exp $ // // $Log: DisCoApplet.java,v $ // Revision 1.1 1996/05/09 07:55:08 rike // Initial revision // // Imports import java.util.*; import java.awt.*; import java.applet.Applet; // Class DisCoAction is an abstract base class for the actual // action classes. abstract class DisCoAction { int x; // coordinates of the action int y; int width = 100; // size of the box representing the action int height = 60; int part_count = 2; // number of participant roles in the action boolean enabled; int currentRole; // index of the role currently selected // from the action String name; // A method that draws the action on a given Graphics public void draw(Graphics g) { Color myFillColor = (enabled ? (Animation.activeAction == this ? Color.red : Color.white) : Color.yellow); g.setColor(myFillColor); g.fillRect(x, y, width, height); g.setColor(Color.black); g.drawRect(x, y, width, height/2); g.drawRect(x, y+height/2, width/2, height/2); g.drawRect(x+width/2, y+height/2, width/2, height/2); g.drawString(name, x+5, y+10); g.drawString("P1", x+5, y + height/2 + 10); g.drawString("Proc", x+5, y + height/2 + 25); g.drawString("P2", x + width/2 + 5, y + height/2 + 10); g.drawString("Proc", x + width/2 + 5, y + height/2 + 25); } // A method that checks given coordinates are inside the action // and if they are, performs the necessary tasks. public boolean wasClicked(int cx, int cy) { if (x <= cx && cx <= x + width && y <= cy && cy <= y + height) { if(cy <= y + height/2){ select(); } else if(cx <= x + width/2){ selectRole(0); } else{ selectRole(1); } return true; } return false; } // A method to be called when the action is selected. public void select() { if (enabled) { Animation.activeAction = this; DisCoObject processes[] = Animation.objs; int i; for (i = 0; i < processes.length; i++) { processes[i].suitable = false; } } } // The abstract methods. enableIfPossible corresponds to // checking the guard of the action. public abstract void enableIfPossible(DisCoObject processes[]); public abstract void selectRole(int whichRole); public abstract void execute(DisCoObject proc); } // The actual action class representing action Communicate class CommunicateAction extends DisCoAction { // a method that checks the guard condition and if it is true, // enables the action public void enableIfPossible(DisCoObject processes[]) { int i; for(i=0; i < processes.length; i++) { if(processes[i].next != null) { if(processes[i].sorted && !processes[i].next.sorted && processes[i].X <= processes[i].next.X) { enabled = true; return; } else { enabled = false; } } else { enabled = false; } } } // A method that is called when a participant role is selected. // Suitability of objects for this role is also checked and // updated in the method. public void selectRole(int whichRole) { currentRole = whichRole; if (Animation.activeAction == this) { DisCoObject processes[] = Animation.objs; int i; for(i = 0; i < processes.length; i++) { if(whichRole == 0) { if(processes[i].next != null) { if(processes[i].sorted && !processes[i].next.sorted && processes[i].X <= processes[i].next.X) { processes[i].suitable = true; } else { processes[i].suitable = false; } } } else { if(processes[i].prev != null) { if(processes[i].prev.sorted && !processes[i].sorted && processes[i].prev.X <= processes[i].X) { processes[i].suitable = true; } else { processes[i].suitable = false; } } } } } } // A method for executing the action. public void execute(DisCoObject proc) { if (currentRole == 0){ proc.next.sorted = true; } else { proc.sorted = true; } } } // ExchangeAction is similar to CommunicateAction apart from // the guard condition and actual effect of execution. class ExchangeAction extends DisCoAction { public void enableIfPossible(DisCoObject processes[]) { int i; for(i=0; i < processes.length; i++) { if(processes[i].next != null) { if(processes[i].X > processes[i].next.X) { enabled = true; return; } else { enabled = false; } } else { enabled = false; } } } public void selectRole(int whichRole) { currentRole = whichRole; if (Animation.activeAction == this) { DisCoObject processes[] = Animation.objs; int i; for(i = 0; i < processes.length; i++) { if(whichRole == 0) { if(processes[i].next != null) { if(processes[i].X > processes[i].next.X) { processes[i].suitable = true; } else { processes[i].suitable = false; } } } else { if(processes[i].prev != null) { if(processes[i].prev.X > processes[i].X) { processes[i].suitable = true; } else { processes[i].suitable = false; } } } } } } public void execute(DisCoObject proc) { int temp; if (currentRole == 0){ temp = proc.X; proc.X = proc.next.X; proc.next.X = temp; if (proc.prev != null) { proc.sorted = false; } } else { temp = proc.X; proc.X = proc.prev.X; proc.prev.X = temp; if (proc.prev.prev != null) { proc.prev.sorted = false; } } } } // Class DisCoObject represents the objects. Since there is only // one class of objects in this specification, we don't bother to // define an abstract base class for all object classes. class DisCoObject { int x; // coordinates and size int y; int width = 50; int height = 40; String name; int X; // variables of the object boolean sorted; boolean suitable; // true if this object is a suitable // participant for the currently selected // action and role DisCoObject prev; // References to the previous and next DisCoObject next; // objects in the chain. This information // is creation specific. // Draw the object. public void draw(Graphics g, Color fillColor) { g.setColor(fillColor); g.fillRect(x, y, width, height); if (suitable) { g.setColor(Color.red); g.drawRect(x-1, y-1, width+2, height+2); g.drawRect(x-2, y-2, width+4, height+4); g.drawRect(x-3, y-3, width+6, height+6); } else { g.setColor(Color.black); } g.drawRect(x, y, width, height); g.drawString(name, x+5, y+10); String num = new String(); g.drawString(num.valueOf(X), x + 5, y + height/2 + 5); if (sorted) { g.drawString("sorted", x+5, y + height/2 + 15); } } // Check if this object was clicked and call select if it was. public boolean wasClicked(int cx, int cy) { if (x <= cx && cx <= x + width && y <= cy && cy <= y + height) { select(); return true; } else { return false; } } // If this object is suitable, execute the currently active action. // In this specification, the other participant of the action // is always implicitly known. public void select() { if (suitable) { Animation.activeAction.execute(this); DisCoObject[] processes = Animation.objs; int i; for (i= 0; i < processes.length; i++) { processes[i].suitable = false; } Animation.activeAction = null; } } } // Animation is a specification specific class that keeps track of // all "global" data, such as action and object tables and currently // active actions. It also initializes the objects and actions and // draws the whole animation. Also automatic execution is done\ // in this class. class Animation extends Panel { // some constants static final int OBJ_COUNT = 6; static final int ACTION_COUNT = 2; // some static data public static DisCoObject objs[] = new DisCoObject[OBJ_COUNT]; public static DisCoAction actions[] = new DisCoAction[ACTION_COUNT]; public static DisCoAction activeAction; public static Random randomgen = new Random(); public static Applet myAppletContext; public boolean auto = false; // some internal use data private boolean actionExecuting; private DisCoObject participant1; private DisCoObject participant2; // the constructor Animation(Applet a) { myAppletContext = a; } // initialization public void init() { objs[0] = new DisCoObject(); objs[0].X = Math.abs(randomgen.nextInt())%100; objs[0].name = "P1"; objs[0].x = 50; objs[0].y = 300; objs[0].sorted = true; objs[1] = new DisCoObject(); objs[1].X = Math.abs(randomgen.nextInt())%100; objs[1].name = "P2"; objs[1].x = 120; objs[1].y = 300; objs[1].sorted = false; objs[2] = new DisCoObject(); objs[2].X = Math.abs(randomgen.nextInt())%100; objs[2].name = "P3"; objs[2].x = 190; objs[2].y = 300; objs[2].sorted = false; objs[3] = new DisCoObject(); objs[3].X = Math.abs(randomgen.nextInt())%100; objs[3].name = "P4"; objs[3].x = 260; objs[3].y = 300; objs[3].sorted = false; objs[4] = new DisCoObject(); objs[4].X = Math.abs(randomgen.nextInt())%100; objs[4].name = "P5"; objs[4].x = 330; objs[4].y = 300; objs[4].sorted = false; objs[5] = new DisCoObject(); objs[5].X = Math.abs(randomgen.nextInt())%100; objs[5].name = "P6"; objs[5].x = 400; objs[5].y = 300; objs[5].sorted = false; int i; for (i = 0; i < 6; i++) { objs[i].prev = (i > 0) ? objs[i-1] : null; objs[i].next = (i < 5) ? objs[i+1] : null; } actions[0] = new ExchangeAction(); actions[0].name = "Exchange"; actions[0].x = 100; actions[0].y = 100; actions[1] = new CommunicateAction(); actions[1].name = "Communicate"; actions[1].x = 300; actions[1].y = 100; for(i = 0; i < 2; i++) { actions[i].enableIfPossible(objs); } } public DisCoObject[] getProcesses() { return objs; } public DisCoAction[] getActions() { return actions; } // Automatic execution. public boolean autoExec() { if (!auto) { return false; } // check enabled objects for (int l= 0; l < actions.length; l++) { actions[l].enableIfPossible(objs); } // select one of them (randomly) int i = 0; if (actions[0].enabled && actions[1].enabled) { i = Math.abs(randomgen.nextInt())%2; } else if (actions[0].enabled) { i = 0; } else if (actions[1].enabled) { i = 1; } else { return false; } int count = 0; activeAction = actions[i]; // we can always select role 0 without loss of nondeterminism, // because the actions are "symmetric" actions[i].selectRole(0); // count suitable objects for (int k=0; k < OBJ_COUNT; k++) { if (objs[k].suitable) { count++; } } // select an object randomly int temp = Math.abs(randomgen.nextInt())%count + 1; count = 0; int j = 0; while ( count < temp ) { if (objs[j].suitable){ count++; } j++; } // execute the action actions[i].execute(objs[j-1]); actionExecuting = true; participant1 = objs[j-1]; participant2 = objs[j]; myAppletContext.repaint(); return true; } // draw a "frame" of the animation public void paint(Graphics g) { int i; for (i= 0; i < actions.length; i++) { actions[i].enableIfPossible(objs); } Color fillColor = Color.yellow; for (i=0; i < OBJ_COUNT; i++) { objs[i].draw(g, fillColor); } for (i=0; i < ACTION_COUNT; i++) { actions[i].draw(g); } if (actionExecuting) { g.setColor(Color.red); g.drawLine(participant1.x,participant1.y, activeAction.x,activeAction.y); g.drawLine(participant2.x,participant2.y, activeAction.x,activeAction.y); actionExecuting = false; } } // event handling public boolean mouseDown(Event evt, int x, int y) { int i = 0; while ( i < OBJ_COUNT && !objs[i].wasClicked(x,y)){ i++; } if (i == OBJ_COUNT){ i = 0; while ( i < ACTION_COUNT && !actions[i].wasClicked(x,y)){ i++; } } return true; } } // The actual applet class public class DisCoApplet extends Applet { Animation anim = new Animation(this); int height=350, width = 500; public void init(){ resize(width,height); anim.init(); add(new Label("Automatic execution:")); add(new Button("Step")); } public void start() { repaint(); } public void paint(Graphics g) { anim.paint(g); } public boolean mouseDown(Event evt, int x, int y) { anim.mouseDown(evt, x, y); return true; } public boolean mouseDrag(Event evt, int x, int y) { anim.mouseDrag(evt, x, y); return true; } public boolean mouseUp(Event evt, int x, int y) { anim.mouseUp(evt, x, y); repaint(); return true; } public boolean action(Event evt, Object arg) { if ("Step".equals(arg)) { anim.auto = true; anim.autoExec(); repaint(); return true; } return false; } }