7.7 Event Multicaster

 < Day Day Up > 



7.7 Event Multicaster

The animator implemented in Exhibit 14 (Program7.5c) is correct but can be optimized greatly. The cloning of a vector is a potentially expensive operation, and being called in the dispatch method ensures that it is called often. Also, the use of the vector requires the vector object's lock to be obtained on each call to a vector method, even though the vector is encapsulated in the source object and so is safe even without holding the object lock. It would be much faster if we had some way to decrease the number of times the vector needed to be cloned and handled. The key to doing this is to remember that the reason for the vector being cloned was to create an immutable vector for the dispatcher method to use. So, what is needed is to create the immutable vector in the add and remove methods when an object is added or removed from the vector and not in the dispatch method, where it is created each time an event is dispatched. Because objects are added and removed much less frequently than they are dispatched, the potential savings in computer resources could be quite large.

Exhibit 14: Program7.5c: Animator Class

start example

 import java.awt.*; import java.awt.event.*; import java.util.*; import javax.swing.*; /**  *  Animator simply creates all the objects in this project, and  *  then goes into an infinite loop (here only to 300) that  *  waits some small period of time and then redraws all of  *  the objects that have registered themselves with the  *  DrawTable.  */ public class Animator extends JPanel implements Runnable {   private Vector elementsToDraw = new Vector();   private long sleepTime = 100;   boolean animatorStopped = true, firstTime = true;   ControlFrame controlFrame;   JFrame animFrame;   /**    *  Constructor that creates the JFrame for the animator. Note    *  that the JFrame is shown in the show() method because this    *  starts the GUI thread. Starting the thread in the    *  constructor can lead to a race condition.    */   public Animator() {     // Create the control Frame.   controlFrame = new ControlFrame(this);     // set up the frame to draw in.     animFrame = new JFrame("Generic Animator");     Container animContainer = animFrame.getContentPane();     animContainer.add(this);     animFrame.setSize(700, 450);     animFrame.setLocation(0,100);   }   /**    *  setVisible is called to display or hide the animator. Note    *  that only display = true is implemented, and this function    *  only works at this point if it is called once. It is left    *  as an exercise to implement it correctly. If display =    *  false, the Control Thread must be suspended. If    *  display = true, the control thread should be started only    *  the first time; after that it should be unsuspended. This    *  can be accomplished by using control variables in the    *  paint method for Program7.4 and after and should not be    *  done using the suspend and resume methods.    */   public void setVisible(boolean display) {     if (display = = true) {       if (firstTime) {         firstTime = false;         // Show the control Frame         controlFrame.setVisible(true);         // Show the animator. This starts the GUI thread.         animFrame.setVisible(true);         // Put the animator in another thread so that the         // calling object can continue.         (new Thread(this)).start();       }     }   }   /**    *  Thread that runs the animator. This thread sleeps for some    *  amount of time specified by sleepTime, then calls repaint,    *  which forces a call to the paint method, but in the GUI    *  thread. Note that the animatorStopped button allows the    *  animator to single step and pause the animation. The    *  notify is done in the control frame from the button.    */   public void run() {     while (true) {       try {         synchronized(this) {           if (animatorStopped = = true) {             wait();           }         }         if (animatorStopped ! = true)           Thread.sleep(sleepTime);       } catch (InterruptedException e) {         System.out.println("Program Interrupted");         System.exit(0);       }       repaint();     }   }   /**    *  The paint method is run in the GUI thread. The repaint   // command causes this to be called, and it passes the   // graphics object g to each of the registered objects to   // have them draw themselves.    */   public void paint(Graphics g) {     Vector v;     synchronized(this) {       v = (Vector) elementsToDraw.clone();     }     super.paint(g);     DrawEvent de = new DrawEvent(this, g);     Enumeration e = v.elements();     while (e.hasMoreElements())       ((DrawListener) e.nextElement()).draw(de);   }   /**    *  addElement adds each drawable to the vector for use by    *  the DrawElements method. You need to write this method.    */   public synchronized void addDrawListener(DrawListener d) {     elementsToDraw.addElement(d);   }   /**    *  removeElement is used to remove drawables from the vector.    *  While not needed to make the animator work, you need to    *  implement this method for the project.    */   public synchronized void removeDrawListener(DrawListener d) {     elementsToDraw.removeElement(d);   }   /**    *  This is an inner class that implements the control panel    *  for the application. It is not important to the behavior    *  of the animator component, which can be understood without    *  understanding this control panel.    *    *  Note that we do not need to synchronize any of these    *  methods because the values they change can only be changed    *  in the GUI thread and are read only elsewhere, so no race    *  conditions exist.    */   private class ControlFrame {     Animator ca;     static final int RUNNING = 0;     static final int WAITING = 1;     int state = WAITING;     JFrame controlFrame = new JFrame("Controller");     public ControlFrame(Animator Parent) {       ca = Parent;       controlFrame = new JFrame("Controller");       Container controlContainer = controlFrame.getContentPane           ();       controlContainer.setLayout(new FlowLayout());       final JButton startButton = new JButton("Start");         startButton.addActionListener(new ActionListener() {         public void actionPerformed(ActionEvent e) {           if (state = = RUNNING) {             state = WAITING;             startButton.setText("Start");             ca.animatorStopped = true;           }           else {             state = RUNNING;             startButton.setText("Stop");             synchronized (ca) {               ca.animatorStopped = false;               ca.notify();             }           }         }       });       controlContainer.add(startButton);       final JButton stepButton = new JButton("Step");       stepButton.addActionListener(new ActionListener() {         public void actionPerformed(ActionEvent e) {           if (state = = RUNNING) {             state = WAITING;             startButton.setText("Start");             ca.animatorStopped = true;           }           synchronized (ca) {             ca.notify();           }         }       });       controlContainer.add(stepButton);       JButton exitButton = new JButton("Exit");       exitButton.addActionListener(new ActionListener() {         public void actionPerformed(ActionEvent e) {           System.exit(0);         }       });       controlContainer.add(exitButton);       controlContainer.add(new Label("Animator Speed"));       final JScrollBar speedControl = new JScrollBar(JScrollBar.           HORIZONTAL, 500, 25, 100, 1000);       speedControl.addAdjustmentListener(new AdjustmentListener           () {         public void adjustmentValueChanged(AdjustmentEvent e) {           ca.sleepTime = 1000 - e.getValue();         }       });       controlContainer.add(speedControl);       controlFrame.setSize(500, 100);     }     public void setVisible(boolean show) {       controlFrame.setVisible(show);     }   } } 

end example

Exhibit 15: Program7.5d: MoveObjects Program with the Corrected Animator Component

start example

 import java.awt.*; public class MoveObjects {   public static void main(String args[]) {     Animator animator = new Animator();     MoveBall mb = new MoveBall(MoveBall.UP, 1);     animator.addDrawListener(mb);     MoveRect mr = new MoveRect(MoveRect.DOWN, 2);     animator.addDrawListener(mr);     animator.setVisible(true);   } } // Two classes, MoveBall and MoveRect, are almost exactly the same. // The constructor adds them to the animator, and the draw handles // the path on which to move them and the drawing of them. // class MoveBall implements DrawListener {   static final int UP = 1;   static final int DOWN = 0;   private int direction = DOWN;   private Path myPath;   private Point myPosition = new Point(10,10);   private int myNumber;   /**    *Constructor - Simply register myself with the animator.    */   public MoveBall(int direction, int myNumber) {     this.direction = direction;     this.myNumber = myNumber;   }   /**    *  Draw is called each time through the animator loop to draw    *  the object. It simply uses the path to calculate the    *  position of this object and then draws itself at that    *  position.    */   public void draw(DrawEvent de) {     Graphics g = de.getGraphics();     if (myPath ! = null && myPath.hasMoreSteps())       myPosition = myPath.nextPosition();     else {       // Get a random number of steps to make the balls move at       // different speeds. Note that there has to be at least one       // step in each path, but for appearances we used at least       // ten steps.       int numberOfSteps = (int) (10.0 + (Math.random() * 100.0));       if (direction = = DOWN) {         myPath = new StraightLinePath(410, 410, 10, 10,             numberOfSteps);         myPosition = myPath.nextPosition();         direction = UP;       }       else {         myPath = new StraightLinePath(10, 10, 410, 410,             numberOfSteps);         myPosition = myPath.nextPosition();         direction = DOWN;       }     }     g.setColor(Color.RED);     g.fillOval((int)myPosition.getX(), (int)myPosition.getY(),         15, 15);     g.setColor(Color.BLACK);     g.drawString("" + myNumber,       (int)myPosition.getX()+3, (int)myPosition.getY()+12);   } } class MoveRect implements DrawListener {   static final int UP = 1;   static final int DOWN = 0;   private int direction = DOWN;   private Path myPath;   private Point myPosition = new Point(10,10);   private int myNumber;   /**    *Constructor - Simply register myself with the animator.    */   public MoveRect(int direction, int myNumber) {     this.direction = direction;     this.myNumber = myNumber;   }   /**    *  Draw is called each time through the animator loop to draw    *  the object. It simply uses the path to calculate the    *  position of this object and then draws itself at that    *  position.    */   public void draw(DrawEvent de) {     Graphics g = de.getGraphics();     if (myPath ! = null && myPath.hasMoreSteps())       myPosition = myPath.nextPosition();     else {       // Get a random number of steps to make the balls move at       // different speeds. Note that there has to be at least one step       // step in each path, but for appearances we used at least       // ten steps.       int numberOfSteps = (int) (10.0 + (Math.random() *          100.0));       if (direction = = DOWN) {         myPath = new StraightLinePath(410, 410, 10, 10,             numberOfSteps);         myPosition = myPath.nextPosition();         direction = UP;       }       else {         myPath = new StraightLinePath(10, 10, 410, 410,           numberOfSteps);         myPosition = myPath.nextPosition();         direction = DOWN;       }     }     g.setColor(Color.YELLOW);     g.fillRect((int)myPosition.getX(), (int)myPosition.getY(),         15, 15);     g.setColor(Color.BLACK);     g.drawString("" + myNumber,       (int)myPosition.getX()+3, (int)myPosition.getY()+12);   } } 

end example

The way this is done in the Java AWT is through a class called AWTEventMulticaster, so the generic application of this technique will be called a Multicaster in this discussion. An example of a DrawEventMulticaster in the animator is given in Exhibit 16 (Program7.6a) and is used in Exhibit 17 (Program7.6b). It works because the addDrawListener and removeDrawListener methods no longer modify the elementsToDraw vector, but instead replace the DrawListener DrawEventMulticaster each time an element is added or removed. The Iterator created in the animator's paint method points to the original DrawEventMulticaster, not the new one, so a change to the DrawListener variable does not change the Iterator used and so does not affect the dispatching of events. This programming is now safe and much more efficient than the event dispatching in Exhibit 14 (Program7.5c).

Exhibit 16: Program7.6a: DrawEventMulticaster

start example

 import java.util.*; public class DrawEventMulticaster implements DrawListener {   ArrayList elementsToDraw = new ArrayList();   protected DrawEventMulticaster(DrawListener a, DrawListener b)       {     if (a ! = null) {       if (a instanceof DrawEventMulticaster) {         elementsToDraw.addAll(((DrawEventMulticaster)a).           elementsToDraw);       }       else {         elementsToDraw.add(a);       }     }     if (b ! = null) {       if (b instanceof Collection) {         elementsToDraw.addAll((Collection)b);       }       else {         elementsToDraw.add(b);       }     }   }   public static DrawListener add(DrawListener a, DrawListener b)       {     return new DrawEventMulticaster(a,b);   }   public static DrawListener remove(DrawListener a, DrawListener b)       {     DrawEventMulticaster dem = new DrawEventMulticaster(a,       null);     (dem.elementsToDraw).remove(b);     return dem;   }   public void draw(DrawEvent e) {     if (elementsToDraw ! = null) {       Iterator i = elementsToDraw.iterator();       while (i.hasNext()) {         DrawListener dl = (DrawListener)i.next();         dl.draw(e);       }     }   } } 

end example

Exhibit 17: Program7.6b: Animator Using the DrawEventMulticaster

start example

 import java.awt.*; import java.awt.event.*; import java.util.*; import javax.swing.*; /**  *Animator simply creates all the objects in this project and  *then goes into an infinite loop (here only to 300) that  *waits some small period of time and then redraws all of the  *objects that have registered themselves with the DrawTable.  */ public class Animator extends JPanel implements Runnable {   DrawListener drawListener = null;   long sleepTime = 100;   boolean animatorStopped = true, firstTime = true;   ControlFrame controlFrame;   JFrame animFrame;   /**    *Constructor that creates the JFrame for the animator. Note    *that the JFrame is shown in the show() method because this    *starts the GUI thread. Starting the thread in the    *constructor can lead to a race condition.    */   public Animator() {     // Create the control Frame.     controlFrame = new ControlFrame(this);     // Set up the frame to draw in.     animFrame = new JFrame("Generic Animator");     Container animContainer = animFrame.getContentPane();     animContainer.add(this);     animFrame.setSize(700, 450);     animFrame.setLocation(0,100);   }   /**    *setVisible is called to display or hide the animator. Note    *that only display = true is implemented, and this function    *only works at this point if it is called once. It is left    *as an exercise to implement it correctly. If display =    *false, the Control Thread must be suspended. If    *display = true, the control thread should be started only    *the first time; after that it should be unsuspended. This    *can be accomplished as using control variables in the    *paint method for Program7.4 and after and should not be    *done using the suspend and resume methods.    */   public void setVisible(boolean display) {     if (display = = true) {       if (firstTime) {         firstTime = false;         // Show the control Frame         controlFrame.setVisible(true);         // Show the animator. This starts the GUI thread.         animFrame.setVisible(true);         // Put the animator in another thread so that the         // calling object can continue.         (new Thread(this)).start();       }     }   }   /**    *Thread that runs the animator. This thread sleeps for some    *amount of time specified by sleepTime, then calls repaint,    *which forces a call to the paint method, but in the GUI    *thread. Note that the animatorStopped button allows the    *animator to single step and pause the animation. The    *notify is done in the control frame from the button.    */   public void run() {     while (true) {       try {         synchronized(this) {           if (animatorStopped = = true) {             wait();           }         }         if (animatorStopped ! = true)           Thread.sleep(sleepTime);       } catch (InterruptedException e) {         System.out.println("Program Interrupted");         System.exit(0);       }       repaint();     }   }   /** The paint method is run in the GUI thread. The repaint   // command causes this to be called, and it passes the   // graphics object g to each of the registered objects to   // have them draw themselves.   */   public void paint(Graphics g) {     super.paint(g);     DrawEvent de = new DrawEvent(this, g);     if (drawListener ! = null)       drawListener.draw(de);   }   /**    *addElement adds each drawable to the vector for use by the    *DrawElements method. You need to write this method.    */   public synchronized void addDrawListener(DrawListener d) {     drawListener = DrawEventMulticaster.add(drawListener, d) ;   }   /**    *removeElement is used to remove drawables from the vector.    *While not needed to make the animator work, you need to    *implement this method for the project.    */   public synchronized void removeDrawListener(DrawListener d) {     drawListener = DrawEventMulticaster.remove(drawListener, d)         ;   }   /**    *This is an inner class that implements the control panel    *for the application. It is not important to the behavior    *of the animator component, which can be understood without    *understanding this control panel.    */   private class ControlFrame {     Animator ca;     static final int RUNNING = 0;     static final int WAITING = 1;     int state = WAITING;     JFrame controlFrame;     public ControlFrame(Animator Parent) {       ca = Parent;       controlFrame = new JFrame("Controller");       Container controlContainer = controlFrame.getContent- Pane();       controlContainer.setLayout(new FlowLayout());       final JButton startButton = new JButton("Start");         startButton.addActionListener(new ActionListener() {         public void actionPerformed(ActionEvent e) {           if (state = = RUNNING) {             state = WAITING;             startButton.setText("Start");             ca.animatorStopped = true;           }           else {             state = RUNNING;             startButton.setText("Stop");             synchronized (ca) {               ca.animatorStopped = false;               ca.notify();             }           }         }       });       controlContainer.add(startButton);       final JButton stepButton = new JButton("Step");       stepButton.addActionListener(new ActionListener() {         public void actionPerformed(ActionEvent e) {           if (state = = RUNNING) {             state = WAITING;             startButton.setText("Start");             ca.animatorStopped = true;           }           synchronized(ca) {             ca.notify();           }         }       });       controlContainer.add(stepButton);       JButton exitButton = new JButton("Exit");       exitButton.addActionListener(new ActionListener() {         public void actionPerformed(ActionEvent e) {           System.exit(0);         }       });       controlContainer.add(exitButton);       controlContainer.add(new Label("Animator Speed"));       final JScrollBar speedControl = new JScrollBar(JScroll- Bar.          HORIZONTAL, 500, 25, 100, 1000);       speedControl.addAdjustmentListener(new AdjustmentLis- tener          () {         public void adjustmentValueChanged(AdjustmentEvent e) {           ca.sleepTime = 1000 - e.getValue();         }       });       controlContainer.add(speedControl);       controlFrame.setSize(500, 100);     }     public void setVisible(boolean show) {       controlFrame.setVisible(show);     }   } } 

end example



 < Day Day Up > 



Creating Components. Object Oriented, Concurrent, and Distributed Computing in Java
The .NET Developers Guide to Directory Services Programming
ISBN: 849314992
EAN: 2147483647
Year: 2003
Pages: 162

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net