JVM Bug possibly found in abstract button
Possibly there exists a bug in the abstract button classes.
Please confirm.
If a JButton or derivative has a new ImageIcon set as its icon, then disabling it will cause a null pointer.
Also, if an imageIcon is set with a valid image, and then that image is changed, the enabled icon for the jbutton will update, but the disabled icon will not.
publicclass LFCrashTestextends JPanel{
public LFCrashTest(){
ImageIcon ic =new ImageIcon();
final JButton b =new JButton(ic);
add(b);
b.addActionListener(new ActionListener(){
publicvoid actionPerformed(ActionEvent e){
b.setEnabled(false);
}
});
}
publicstaticvoid main(String[] args){
SwingUtilities.invokeLater(new Runnable(){
publicvoid run(){
LFCrashTest test =new LFCrashTest();
JFrame f =new JFrame("empty icon crash test");
f.add(test);
f.pack();
f.setVisible(true);
}
});
}
}
[2055 byte] By [
BobJandala] at [2007-11-27 8:36:48]

# 1
I can confirm the NPE.
This seems to be LNF dependent though, have tried this with Metal, Motif and Windows LNFs, which all result in throwing an NPE when attempting to create a disabled image.
Metal has it's own method for generating disabled images, but both Motif and Windows use javax.swing.GrayFilter.createDisabledImage(Image).
The NPE happens because of calling img.getSource() without checking whether img is null or not.
The empty constructor for ImageIcon is documented as "Creates an uninitialized image icon.".
I would say that the real bug is that you can actually add an uninitialized ImageIcon to a JButton.
dwga at 2007-7-12 20:33:51 >

# 2
Hey, thanks for your quick reply.
I think (and should have said so) that the bug is two fold:
Firstly the failure to check a null image from the imageIcon.
Secondly to no check if the disabled icon is up to date with the current icon.
My opinion is that being able to set uninitialized ImageIcons to buttons is beneficial, because it allows for changing of the imageIcon image to propogate thru to all the components that have it set as their icon.
Altho I suppose that g.drawImage could be used instead, but Imageicon has some other nice features, like the built in image tracker.
Thanks again.
# 3
> Secondly to no check if the disabled icon is up to
> date with the current icon.
>
I don't think this is a bug. When you set an Icon to button in the constructor the same image is used for both the enabled and disabled button. When you update the image you use setIcon() and setDisabledIcon() as separate calls. If you don't use setDisabledIcon() then you keep the original disabled icon i.e. the one the button was constructed with.
# 4
Yeah, but what if the disabled icon was auto generated from the enabled one ?Doesn't it stand to reason that in that case, and that case only, it should check if the root image changed, and update accordingly ?
# 5
> Yeah, but what if the disabled icon was auto> generated from the enabled one ?I don't understand!
# 6
Well,
like dwg said in the first reply, if you didn't specifically set a disabled icon, then it will run a gray (or grey, depending on which army took over your part of the world) filter.
In this case, it is my opinion that if you change the original, then the auto generated one, that is, the greyed out version of the enabled icon you set originally, should also change to a greyed out version of the new icon.
Cheers.
# 7
> In this case, it is my opinion that if you change the
> original, then the auto generated one, that is, the
> greyed out version of the enabled icon you set
> originally, should also change to a greyed out
> version of the new icon.
>
I have just done some checks and it does seem to do that in 1.6 .
# 8
I think what you did was changed the icon explicitly with setIcon( blah ) or similar ?
I was meaning that you change the image contained in the ImageIcon.
For the enabled icon, this auto updates, but not so for the disabled, even in 1.6
It could always be I'm doing something stupid tho.......
# 9
> I think what you did was changed the icon explicitly
> with setIcon( blah ) or similar ?
Yes!
>
> I was meaning that you change the image contained in
> the ImageIcon.
That is a very different matter. I have never done this and probably never will. To be safe I would create a wrapper Icon and update the wrapper. Of course this would not post any events so the screen would not get updated unless I forced a repaint();
>
> For the enabled icon, this auto updates, but not so
> for the disabled, even in 1.6
This does sound like a bug then. Can you create a self contained example that shows the problem.
# 10
> > For the enabled icon, this auto updates, but not
> so
> > for the disabled, even in 1.6
>
> This does sound like a bug then. Can you create a
> self contained example that shows the problem.
I have changed my mind. For 1.6 the following, based on your code, does exactly as you say it should.
import java.awt.event.*;
import javax.swing.*;
import java.net.*;
public class LFCrashTest extends JPanel
{
private static ImageIcon getIcon(String name)
{
try
{
final String imageRoot = "http://www.stat.uga.edu/images/new_images/fracons/";
return new ImageIcon(new URL(imageRoot + name));
}
catch (Exception e)
{
throw new IllegalArgumentException("Invalid image icon name" + name, e);
}
}
public LFCrashTest()
{
final ImageIcon ic = getIcon("EYEFLOW0.GIF");
final JButton b = new JButton(ic);
final JButton b1 = new JButton(ic);
add(b);
add(b1);
b.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
ic.setImage(getIcon("BAY0.GIF").getImage());
b.setEnabled(false);
b1.setEnabled(false);
}
});
}
public static void main(String[] args)
{
System.out.println(System.getProperty("java.version"));
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
LFCrashTest test = new LFCrashTest();
JFrame f = new JFrame("empty icon crash test");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(test);
f.pack();
f.setVisible(true);
}
});
}
}
# 11
Your code also works as expected on 1.5.0_06.
However after I changed it to the following it didn't work.
import java.awt.event.*;
import javax.swing.*;
import java.net.*;
public class LFCrashTest extends JPanel
{
private static int count = 1;
private static ImageIcon getIcon(String name)
{
try
{
final String imageRoot = "http://www.stat.uga.edu/images/new_images/fracons/";
return new ImageIcon(new URL(imageRoot + name));
}
catch (Exception e)
{
throw new IllegalArgumentException("Invalid image icon name" + name, e);
}
}
public LFCrashTest()
{
final ImageIcon ic = getIcon("EYEFLOW0.GIF");
final JButton b = new JButton(ic);
final JButton b1 = new JButton(ic);
b1.setEnabled(false);
add(b);
add(b1);
ActionListener action = new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
if (count++%2 == 0)
ic.setImage(getIcon("BAY0.GIF").getImage());
else
ic.setImage(getIcon("EYEFLOW0.GIF").getImage());
b.setEnabled(!b.isEnabled());
b1.setEnabled(!b1.isEnabled());
}
};
b.addActionListener(action);
b1.addActionListener(action);
}
public static void main(String[] args)
{
System.out.println(System.getProperty("java.version"));
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
LFCrashTest test = new LFCrashTest();
JFrame f = new JFrame("empty icon crash test");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(test);
f.pack();
f.setVisible(true);
}
});
}
}
dwga at 2007-7-12 20:33:51 >

# 12
What am I missing? For me it works the same for both 1.5_10 and 1.6_1 and it works as I expect it to work.