wood burning stoves 2.0*
The moose likes Swing / AWT / SWT and the fly likes platform dependence bug Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login


Win a copy of Android Security Essentials Live Lessons this week in the Android forum!
JavaRanch » Java Forums » Java » Swing / AWT / SWT
Bookmark "platform dependence bug" Watch "platform dependence bug" New topic
Author

platform dependence bug

Stephen Bloch
Ranch Hand

Joined: Aug 19, 2003
Posts: 48
I'm teaching CS1, using DrJava and/or BlueJ, and wrote a class named World for the students to subclass for graphical apps. World extends JComponent, and in its constructor it creates a WorldFrame (an inner class extending JFrame) and adds itself to the CENTER of the WorldFrame. World also implements MouseListener, with do-nothing implementations for most of the mouse methods, and a mouseClicked(MouseEvent) that extracts the location from the MouseEvent and passes it to a do-nothing mouseClicked(Point) method, which students are supposed to override. So far so good. It works perfectly on my Macintosh.
When my students (or I) try it on a Windows machine, we get a NullPointerException. The stack trace starts with a line in the middle of World.paint(Graphics):
this.circle1.draw(g2);
I tried using the BlueJ debugger to check that circle1 had been assigned properly, but putting in a breakpoint makes the NullPointerException not happen. Ditto in DrJava: I get a NullPointerException on the same line, but if I put a breakpoint into the method (and immediately continue execution when it stops there) things work just fine.
This is really weird; any suggestions?


SCJP 1.4
Gregg Bolinger
GenRocket Founder
Ranch Hand

Joined: Jul 11, 2001
Posts: 15299
    
    6

How do you help your students who come to you with questions about their programs and they don't show you any of their code?
Suggestion 1: Show us the code. I can't debug an app with no code.
[ April 28, 2004: Message edited by: Gregg Bolinger ]

GenRocket - Experts at Building Test Data
Ernest Friedman-Hill
author and iconoclast
Marshal

Joined: Jul 08, 2003
Posts: 24183
    
  34

My suspicion is that you're doing something along the lines of calling "paint(getGraphics())", rather than calling repaint() and letting the JVM worry about screen painting. If you do this, then there's the risk that the component being painting won't have been physically rendered yet, and getGraphics() can return null. The actual behavior is going to be timing-dependent, so using a debugger will "fix" it, as perhaps may running on a different OS or machine.
How to fix? Unless you've got a really, really good reason to do this, simply don't. Just call repaint() and let the JVM deal with it.
If you really, really need to call paint() explicitly (and if you're not sure, then you don't), then you need to check getGraphics() for a non-null return value.
Finally: note that interactive debuggers are nice, but (especially in a multithreaded environment!) never underestimate the value of a well-placed print statement.


[Jess in Action][AskingGoodQuestions]
Stephen Bloch
Ranch Hand

Joined: Aug 19, 2003
Posts: 48
To Ernest: No, I'm not calling paint(). I'm no Swing expert, but I know that much.
To Gregg: I've just spent half an hour trimming out the inessentials, and now have a much shorter version of the program that USUALLY produces a NullPointerException, but sometimes doesn't (and, of course, NEVER does on the Macintosh, or if there's a breakpoint in the paint() method). No difference whatsoever in the way I invoke it: in BlueJ, I right-click on the "SmallWorld" class, choose "new SmallWorld()" from the pop-up menu, and click "OK". It draws the same picture regardless of whether the NPE happens or not. Code follows. Note that I've taken out a lot of stuff that I considered "good design", but wasn't essential to reproducing the error.
World.java:
import java.awt.*;
import javax.swing.*;
import java.util.*;
// import java.util.logging.*;
import java.awt.event.*;
/**
* World: a base class to allow graphical programming without making
* assertions, Loggers, and the debugger ineffective.
*
* You should be able to extend this class in the same way you used
* to extend MouseApplet, e.g.
* class SampleMouseApplet extends World { ...}
* However, to run it you just need to create a new SampleMouseApplet().
*
* World also provides a logger for you to use
* (e.g. World.log.info("This is an informational message.") .
*
* By default, all Worlds are 650 pixels wide by 400 high, but you
* can easily change this in a subclass by writing your own getDefaultWidth()
* and getDefaultHeight() methods, e.g.
* class SampleMouseApplet extends World {
* public int getDefaultWidth () { return 300; }
* ... }
*
* @author Stephen Bloch
* @version 26 April 2004
*/
public abstract class World extends JComponent // implements MouseListener
{

private WorldFrame theFrame = null;
private static final Color backgroundColor = Color.white;
// public static final Logger log = Logger.getLogger("global");
public static final int DEFAULT_WIDTH = 650;
public static final int DEFAULT_HEIGHT = 400;



public World () {
super();
this.theFrame = new WorldFrame("My World!", DEFAULT_WIDTH, DEFAULT_HEIGHT);
this.setPreferredSize (new Dimension (DEFAULT_WIDTH, DEFAULT_HEIGHT));
this.theFrame.getContentPane ().add (this, BorderLayout.CENTER);
this.theFrame.pack();
// this.addMouseListener (this);
}




public void paint(Graphics g) {
super.paint(g);
}

/*
public void mouseClicked (Point where) {
// log.info("Default mouseClicked(Point) behavior of World is to do nothing.");
}

public void mouseClicked (MouseEvent e) {
this.mouseClicked(e.getPoint());
}

public void mouseEntered (MouseEvent e) {
// do nothing
}
public void mouseExited (MouseEvent e) {
// do nothing
}
public void mousePressed (MouseEvent e) {
// do nothing
}
public void mouseReleased (MouseEvent e) {
// do nothing
}
*/



/**
* WorldFrame: an inner class for the JFrame that contains a World.
* In my first version, World was a kind of JFrame itself; this sorta
* works, but isn't "correct" and leads to coordinate confusion: the
* coordinate system of the JFrame includes its title bar, whereas if we
* put a JComponent inside it, as in the current version, the JComponent's
* coordinate system can start at the top-left corner of the content pane.
* @author Stephen Bloch
* @version 26 April 2004
*/
class WorldFrame extends JFrame
{
/**
* Constructor for objects of class WorldFrame
* @param name the title of the world
* @param initialWidth in pixels
* @param initialHeight in pixels
*/
public WorldFrame(String name, int initialWidth, int initialHeight)
{
super(name);
// this.setSize (initialWidth, initialHeight);
this.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
this.getRootPane().putClientProperty("defeatSystemEventQueueCheck", Boolean.TRUE);
}
}
}

SmallWorld.java:
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Color;
import java.awt.Rectangle;
import java.awt.Point;
public class SmallWorld extends World
{
private Circle myCircle;
/**
* Constructor for SmallWorld objects
*/
public SmallWorld()
{
// initialise instance variables
this.myCircle = new Circle (new Point(100, 70), 20, Color.blue);
}
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D)g;

Rectangle background = new Rectangle (0, 0, this.getWidth(), this.getHeight());
g2.setColor (Color.white);
g2.fill(background);

this.myCircle.draw(g2);
}
}

Circle.java:
import java.awt.Point;
import java.awt.geom.Ellipse2D;
import java.awt.Color;
import java.awt.Graphics2D;
/**
* A Circle, which can appear on the screen (among other tricks).
*
* @author Stephen Bloch
* @version 29 April 2004
*/
public class Circle
{
private int radius;
private Point center;
private Color color;
/**
* Constructor for objects of class Circle
* @param center
* @param radius
* @param color
*/
public Circle(Point center, int radius, Color color)
{
this.center = center; // was being cloned, but I'm stripping out inessentials to track down the NullPointerException.
this.color = color;
this.radius = radius;
}
/**
* Draw the circle on the screen.
*
* @param g2 a Graphics2D context
*/
public void draw (Graphics2D g2)
{
Ellipse2D visibleCircle = this.getVisibleEllipse();
g2.setColor (this.color);
g2.draw (visibleCircle);
}

/**
* make an ellipse to represent the thing on the screen.
* @return an Ellipse2D.Double
*/
private Ellipse2D getVisibleEllipse () {
return new Ellipse2D.Double (this.center.x - this.radius,
this.center.y - this.radius,
this.radius*2, this.radius*2);
}

}
Ernest Friedman-Hill
author and iconoclast
Marshal

Joined: Jul 08, 2003
Posts: 24183
    
  34

The "World" constructor creates an instance of WorldFrame, adds itself to the WorldFrame, and makes the WorldFrame visible. As soon as this happens, paint() may be called.
SmallWorld extends World. When you create an instance of SmallWorld, first the World constructor runs, and then the body of the SmallWorld constructor runs.
So here's the scenario where you get an NPE:
1) World constructor runs.
2) World appears on the screen.
3) World.paint() is called. SmallWorld constructor hasn't run yet.
4) It's overridden by SmallWorld to call Graphics.draw(this.myCircle).
5) Because the body of the SmallWorld constructor hasn't run yet, this.myCircle is null.
6) Boom!
Stephen Bloch
Ranch Hand

Joined: Aug 19, 2003
Posts: 48
> 1) World constructor runs.
> 2) World appears on the screen.
> 3) World.paint() is called. SmallWorld constructor hasn't run yet.
> 4) It's overridden by SmallWorld to call Graphics.draw(this.myCircle).
> 5) Because the body of the SmallWorld constructor hasn't run yet, this.myCircle is null.
> 6) Boom!
OK, that explains it; thanks! And it doesn't happen on the Mac because of some implementation detail affecting when paint() is first called. So to avoid it, I need to avoid making the thing visible until after (or the end of) the subclass constructor.
Is there any way to hide that from my CS1 students when they write SmallWorld? (If not, I can tell them to put this.setVisible(true) at the end of their constructor ... but that's one more bit of magic I'd prefer to avoid if possible.)
 
I agree. Here's the link: http://aspose.com/file-tools
 
subject: platform dependence bug
 
Similar Threads
Passing an ArrayList from one class as a parameter to the constructor of another class
Two versions of java on same system ?
Runtime Changes to Your GUI
problem with popuphandler
Creating mouse rollover for for custom drawn JButton