MIT 6.030:
Introduction to Interactive Programming

Laboratory 6: Communicating applications
Solutions

Contents

Part 1 Part 2

Pre-lab Questions

Q: Design a data repository class that keeps track of an x and a y coordinage and has an additional field of type int. Make sure that your class provides transaction-safe access to its fields. It should be possible to change the coordinates, but not the additional type field. It should also be possible to hand off the x and y coordinates safely.

A: If we were to write a class that had separate getX() and getY() methods, or even separate setX() and setY() methods, then two threads might interleave the various sets and gets so that the x coordinate stored or retrieved is from a different Point than the y coordinate.

Setting both coordinates in a single transaction is easy as long as the method is synchronized appropriately. In order to get both coordinates in a single transaction, however, we will need to return two numbers from a single method call. In order to do this, we need to return a single object that contains both values: a java.awt.Point.

Some people used a Point as a field in their class. Unfortunately, it is very tempting to return this same Point for the getLocation method. If you return the actual Point, as opposed to a copy of that Point), then malicious code could change the data for this class whenever it wanted and cause unsafe transactions. One way to think about this is to answer the question, "If you had a hundred dollar bill, whould you rather give the actual bill to someone else, or give them a photocopy of the bill?". If you give the copy, the other person can maim, spindle, or mutilate it to thier heart's content without damaging your own money.

import java.awt.Point;

/**
 * Location serves as a transaction-safe repository for a pair of
 * coordinates.  There is no way to get or set the coordinates in
 * such a way that the x and y values are inconsistent.
 */
public class Location
{
   private int x;        // x location
   private int y;        // y location

   private int type= 0;  // this seems to serve no purpose in this lab

   /**
    * Constructor: takes initial coordinates and an integer type
    */
   public Location(int x, int y, int type)
   {
      this.x= x;
      this.y= y;
      this.type= type;
   }

   /**
    * Set the coordinates for this location.  This method is synchronized
    * so that only one setLocation() or getLocation() can take place
    * at once, and there is no way to set or get an x value that doesn't
    * belong with the cooresponding y value.
    */
   public synchronized void setLocation(int x, int y)
   {
      this.x= x;
      this.y= y;
   }

   /**
    * Get the coordinates for this location.  This method returns a
    * complete java.awt.Point so that the x and y values are returned
    * together.  Having separate getX() and getY() methods would not
    * be transaction safe.
    */
   public synchronized Point getLocation()
   {
      return new Point(this.x, this.y);
   }
}

Q: Design a class that extends java.awt.Panel and keeps track of a location in screen coordinates. The paint method of this class should draw a filled oval centered at the specified point.

A: This is pretty straightforward. I have ignored the directions and chosen to extend Canvas instead of Panel. This is because a Panel is a Container, meaning that it can contain other java.awt.Components. This class doesn't, and won't, contain any other Components: we just use it to draw on. The basic drawing Component is a Canvas.

import java.awt.*;

/**
 * Displayer provides a method for some other class to specify a pair of
 * coordinates.  Displayer draws a filled oval centered around those
 * coordinates.
 */
public class Displayer extends Canvas
{
   private Location loc;             // We use a Location to safely hold the data
   
   private final int OVALWIDTH= 20;  // Width of the oval
   private fianl int OVALHEIGHT= 10; // Height of the oval

   /**
    * Constructor: on startup, the oval is displayed in the top left corner.
    */
   public Displayer()
   {
      super();
      this.loc= new Location(0, 0, 0);
   }

   /**
    * Set the coordinates for the oval to be displayed, and redraw the
    * Displayer with the oval at the new coordinates.
    */
   public void setLocation(int x, int y)
   {
      this.loc.setLocation(x, y);
      this.repaint();
   }

   /**
    * Paint the oval on the Displayer.
    */
   public void paint(Graphics g)
   {
      // geting the location and painting the oval must be a two-step process.
      Point p= this.loc.getLocation();
      g.fillOval(p.x - this.OVALWIDTH/2, p.y - this.OVALHEIGHT/2,
                 this.OVALWIDTH, this.OVALHEIGHT);
   }
}

Q: Design a class that extends java.awt.Panel and keeps track of the mouse position. That is, it should have a getMouse() method that can be called by other objects and returns and object corresponding to the mouse's current location. You may, of course, define auxilliary objects to help handle the relevant events.

A: Again, this is straightforward. See the previous answer for why we've chosen to extend a Canvas instead of a Panel.

import java.awt.*;
import java.awt.event.*;

/**
 * MouseWatcher listens for mouse motion events, and updates an internal
 * Location based on the current mouse location.  Other classes can get
 * this location using the getMouse() method.
 */
public class MouseWatcher extends Canvas implements MouseMotionListener
{
   private Location loc;  // the current location of the mouse

   /**
   * Constructor: the initial location defaults to the top left corner.
   */
   public MouseWatcher()
   {
      super();
      this.loc= new Location(0, 0, 0);
      this.addMouseMotionListener(this);
   }

   /**
    * This method gets called when the mouse moves.  Update the internal
    * location.
    */
   public void mouseMoved(MouseEvent evt)
   {
      this.loc.setLocation(evt.getX(), evt.getY());
   }

   /**
    * This method is required by the MouseMotionListener interface.  We
    * don't use it. 
    */
   public void mouseDragged(MouseEvent evt)
   {
   }

   /**
    * Return the current location of the mouse, based on where we last
    * saw it in a mouseMoved event.
    */
   public Point getMouse()
   {
      return this.loc.getLocation();
   }
}

Q: Now design a class that combines all of these behaviors. Your new class should extend java.awt.Panel and keep track of the last location where the mouse was depressed, i.e., the last place the mouse was really upset. The paint method of this class should draw a filled oval centered at the specified point.

Bonus: Modify your class sl that it keeps track of the current mouse position instead.

A: This is just a combination of the previous two classes.

import java.awt.*;
import java.awt.event.*;

public class Tracker extends Canvas implements MouseMotionListener
{
   private Location loc;             // We use a Location to safely hold the data
   
   private final int OVALWIDTH= 20;  // Width of the oval
   private fianl int OVALHEIGHT= 10; // Height of the oval

   /**
    * Constructor: on startup, the oval is displayed in the top left corner.
    */
   public Tracker()
   {
      super();
      this.loc= new Location(0, 0, 0);
      this.addMouseMotionListener(this);
   }

   /**
    * This method gets called when the mouse moves.  Update the internal
    * location and repaint the Tracker.
    */
   public void mouseMoved(MouseEvent evt)
   {
      this.loc.setLocation(evt.getX(), evt.getY());
      this.repaint();
   }

   /**
    * This method is required by the MouseMotionListener interface.  We
    * don't use it. 
    */
   public void mouseDragged(MouseEvent evt)
   {
   }

   /**
    * Paint the oval on the Displayer.
    */
   public void paint(Graphics g)
   {
      // geting the location and painting the oval must be a two-step process.
      Point p= this.loc.getLocation();
      g.fillOval(p.x - this.OVALWIDTH/2, p.y - this.OVALHEIGHT/2,
                 this.OVALWIDTH, this.OVALHEIGHT);
   }

}

Laboratory

The laboratory code involves 3 classes: The Location class is used as written. The Tracker class needs the following two extra methods in order to display properly:

public class Tracker....
{
   ...

   public Dimension getMinimumSize()
   {
      return new Dimension(200, 200);
   }

   public Dimension getPreferredSize()
   {
      return new Dimension(400, 400);
   }

   ...
}

Finally, you need a main method. Here is what it would look like inside a separate Main class:

import cs101.awt.*;

public class TrackerMain
{
   public static void main(String[] args)
   {
      Tracker t= new Tracker();
      DefaultFrame f= new DefaultFrame(t);
      f.init();
   }
}
A nicer place to put this method might be inside the Tracker class itself.

Part II

Pre-lab Questions

1. What data does the Cat object need to keep track of?
The Cat needs to keep track of both the Cat and Mouse locations
2. What data does the Cat object get from its GUI?
The location of the pointer on the screen (location of the Cat)
3. How does the Cat object get this data?
When the Cat object registers for mouse motion events, the mouseMoved method will be called with the coordinates of the pointer.
4. What does the Cat do with this data?
Save it away in a transaction-safe object and repaint its canvas.
5. What data does the Cat send to the Mouse?
Messages (text Strings) about whether a Cat click successfully hit the mouse. Optionally, the Cat can send its location to the Mouse.
6. What data does the Cat receive from the Mouse?
The Mouse's location
6m. What data does the Mouse receive from the Cat?
Message Strings, and optionally the location of the Cat.
7. How does the Cat access this data?
The Mouse's location comes in through the readObject method on the Wire.
8. What does the Cat do with this data?
Save it away in a transaction-safe object and repaint its canvas.

1. What methods does the Cat need to have to handle the data above?
2. What data structures does it need?
Two Location objects to hold the Cat andm Mouse location.
3. What subordinate objects does the Cat keep track of/use to handle any of its needs?
See answer to 2
4. What threads can be (simultaneously) running in the Cat?
Graphics/interface threads, and the Wire data handler thread
5. Which methods does each thread (potentially) run?
Graphics/interface thread: mouseMoved and paint, which call cat.setLocation, cat.getLocation, and mouse.getLocation.
Wire data handler: getObject, which calls mouse.setLocation
6. What data is shared across threads?
the Mouse Location object
7. Where is synchronization necessary in order to ensure that your code is transaction safe?
the Mouse get and set location methods.

Laboratory

Both the Cat and the Mouse use the same Location class from Part I. I've implemented a Cat and Mouse where both the Cat and Mouse can see both dots.
import java.awt.*;
import java.awt.event.*;
import cs101.net.*;
import cs101.io.Console;
import cs101.lang.*;

public class MouseCanvas extends Canvas
         implements Animate, MouseMotionListener
{
   private Location mouse;
   private Location cat;
   private Wire wire;

   private final int CATSIZE= 20;
   private final int MOUSESIZE= 10;

   public MouseCanvas()
   {
      this.mouse= new Location(0, 0, 0);
      this.cat= new Location(0, 0, 0);

      this.wire= new ServerWire();

      AnimatorThread anim= new AnimatorThread(this);
      anim.setSleepMinInterval(10);
      anim.setSleepRange(0);
      anim.startExecution();
   }

   public void act()
   {
      Object obj= this.wire.readObject();
      if (obj instanceof String)
      {
         Console.println((String)obj);
      }
      else if (obj instanceof Point)
      {
         Point p= (Point) obj;
         this.cat.setLocation(p.x, p.y);
         this.repaint();
      }
   }

   public void mouseMoved(MouseEvent evt)
   {
      this.mouse.setLocation(evt.getX(), evt.getY());
      this.wire.writeObject(this.mouse.getLocation());
      this.repaint();
   }

   public void mouseDragged(MouseEvent evt)
   {
   }

   public void paint(Graphics g)
   {
      Point p;
      p= this.cat.getLocation();
      g.setColor(Color.black);
      g.fillOval(p.x - this.CATSIZE/2, p.y - this.CATSIZE/2,
                 this.CATSIZE, this.CATSIZE);
      p= this.mouse.getLocation();
      g.setColor(Color.red);
      g.fillOval(p.x - this.MOUSESIZE/2, p.y - this.MOUSESIZE/2,
                 this.MOUSESIZE, this.MOUSESIZE);
   }

   public static void main(String[] args)
   {
      MousePanel mp= new MousePanel();
      DefaultFrame f= new DefaultFrame(mp);
      f.init();
   }

}

import java.awt.*;
import java.awt.event.*;
import cs101.net.*;
import cs101.io.Console;
import cs101.lang.*;

public class CatCanvas extends Canvas
         implements Animate, MouseMotionListener, MouseListener
{
   private Location mouse;
   private Location cat;
   private Wire wire;

   private final int CATSIZE= 20;
   private final int MOUSESIZE= 10;

   public CatCanvas()
   {
      this.mouse= new Location(0, 0, 0);
      this.cat= new Location(0, 0, 0);

      this.wire= new ClientWire();

      AnimatorThread anim= new AnimatorThread(this);
      anim.setSleepMinInterval(10);
      anim.setSleepRange(0);
      anim.startExecution();
   }

   public void act()
   {
      Object obj= this.wire.readObject();
      Point p= (Point) obj;
      this.mouse.setLocation(p.x, p.y);
      this.repaint();
   }

   public void mouseMoved(MouseEvent evt)
   {
      this.cat.setLocation(evt.getX(), evt.getY());
      this.wire.writeObject(this.cat.getLocation());
      this.repaint();
   }

   public void mouseDragged(MouseEvent evt)
   {
   }

   public void mousePressed(MouseEvent evt)
   {
      String message;
      Point catp= this.cat.getLocation();
      Point mousep= this.mouse.getLocation();
      if (Math.abs(mousep.x - catp.x) < this.CATSIZE/2 &&           Math.abs(mousep.y - catp.y) < this.CATSIZE/2)
      {
         message= "Cat got the mouse!";
      }
      else
      {
         message= "Cat missed the mouse.";
      }
      Console.println(message);
      this.wire.writeObject(message);
   }

   public void mouseReleased(MouseEvent evt) {}
   public void mouseEntered(MouseEvent evt) {}
   public void mouseExited(MouseEvent evt) {}
   public void mouseClicked(MouseEvent evt) {}

   public void paint(Graphics g)
   {
      Point p;
      p= this.cat.getLocation();
      g.setColor(Color.black);
      g.fillOval(p.x - this.CATSIZE/2, p.y - this.CATSIZE/2,
                 this.CATSIZE, this.CATSIZE);
      p= this.mouse.getLocation();
      g.setColor(Color.red);
      g.fillOval(p.x - this.MOUSESIZE/2, p.y - this.MOUSESIZE/2,
                 this.MOUSESIZE, this.MOUSESIZE);
   }

   public static void main(String[] args)
   {
      CatPanel cp= new CatPanel();
      DefaultFrame f= new DefaultFrame(cp);
      f.init();
   }

}