Draw a Triangle on the Screen, Part II
This function draws a triangle on the screen, with the points selected by the user clicking three times on the screen. It uses the paint() method from the previous example. Because the paint() routine expects the points to be sorted by x coordinate, this function takes care of that.
Calling the repaint() method of the Applet class (which is actually a method of the Component class, the great-grandparent of Applet) eventually causes the paint() method to be called.
In addition to extending the java.applet.Applet class, as all applets do, the function also implements the MouseListener interface to receive mouse clicks. The only method in this interface that matters here is mousePressed(). This calls the method getPoint() (whose functionality is obvious) on the MouseEvent passed as a parameter. If an applet consumes a MouseEvent (or any event derived from its superclass InputEvent), it notes this by calling the consume() method.
The class calls addMouseListener() during initialization of the applet, and removeMouseListener() during destruction. This is how it registers to receive mouse events. These methods take, as a parameter, an object that implements MouseListener. Because the Triangle class extends MouseListener, it can pass this, that is, a pointer to the instance of the applet class itself, as a parameter.
The class should implement several methods that are not shown to save space. Applets normally implement a method getAppletInfo(), which returns the title and author of the applet. In addition, the MouseListener interface has four other methods: mouseReleased(), mouseClicked(), mouseEntered(), and mouseExited(). All these methods take a MouseEvent as a parameter, but don't need to do anything in this example.
Source Code
1. import java.awt.event.*; 2. import java.awt.*; 3. 4. public class Triangle extends java.applet.Applet 5. implements MouseListener { 6. 7. Point[] pt = new Point[3]; 8. int ptCount = 0; 9. 10. public void init() { 11. addMouseListener(this); 12. } 13. 14. public void paint(Graphics g) { 15. // See previous example for implementation 16. } 17. 18. public void mousePressed(MouseEvent e) { 19. 20. if (ptCount < 3) { 21. pt[ptCount] = new Point(e.getPoint()); 22. if ((ptCount++) == 3) { 23. Point p; 24. 25. // Order the points by x value, so 26. // pt[0] has the lowest x and pt[2] 27. // has the highest. 28. 29. if ((pt[1].x < pt[2].x) && 30. (pt[1].x < pt[0].x)) { 31. p = pt[0]; pt[0] = pt[1]; pt[1] = p; 32. } else if ((pt[2].x < pt[1].x) && 33. (pt[2].x < pt[0].x)) { 34. p = pt[0]; pt[0] = pt[2]; pt[2] = p; 35. } 36. if (pt[1].x > pt[2].x) { 37. p = pt[1]; pt[1] = pt[2]; pt[2] = p; 38. } 39. } 40. } 41. e.consume(); 42. repaint(); 43. } 44. 45. public void destroy() { 46. removeMouseListener(this); 47. }
Suggestions
Look at the code on lines 2938. The comment on lines 2527 states that the goal is to order the points. Is it correct? How would you describe the goal after line 35?
mousePressed() calls repaint() even if this is not the third point selected. Is it correct to assume that paint() is ready to be called in this situation?
Examine the code to swap points on lines 31, 34, and 37. Is it done correctly? How many different inputs would be needed to ensure that all these code lines were covered?
Hints
Walk through the mousePressed() method, passing in the third point equal to (20, 50) and with the following values for member variables:
ptCount == 2 pt[0].x == 0 pt[0].y == 100 pt[1].x == 10 pt[1].y == 75
Explanation of the Bug
The bug is on line 22, which reads as follows:
if ((ptCount++) == 3) {
When using the postfix notation for ++, the expression is evaluated before the addition is done. Therefore, this expression is true only when ptCount is already 3 before it is incremented. However, the if() on line 20 will prevent that entire block of code on lines 2139 from executing if ptCount is 3 or greater. Therefore, the entire block of code from lines 2338 will never execute, and the variables won't ever be sorted. This leads to paint() being called with unordered points (unless the user happens to click them in sorted x order) which causes the algorithm to malfunction.
The code should instead read as follows:
if ((++ptCount) == 3) {
Because the increment is done at the incorrect time, you could consider this an F.location error, or you could describe it as B.expression.