JDK-6272764 : [Mustang b36] NPE from BasicLabelUI.getBaseline if label.font == null
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 6
  • Priority: P2
  • Status: Closed
  • Resolution: Duplicate
  • OS: linux
  • CPU: x86
  • Submitted: 2005-05-18
  • Updated: 2010-04-03
  • Resolved: 2005-05-26
Related Reports
Duplicate :  
Description
Compile and run the following program:

---%<---
package comboboxtest;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Font;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.ListCellRenderer;
import javax.swing.plaf.FontUIResource;
import javax.swing.tree.DefaultTreeCellRenderer;
public class Main extends JFrame {
    private static final boolean RUN_CORRECTLY = false;
    public Main() {
        initComponents();
        combobox.setModel(new DefaultComboBoxModel(new Object[] {"hello", "goodbye"}));
        combobox.setRenderer(new MyRenderer());
        pack();
    }
    // <editor-fold defaultstate="collapsed" desc=" Generated Code ">                          
    private void initComponents() {
        irrelevant = new javax.swing.JLabel();
        combobox = new javax.swing.JComboBox();

        getContentPane().setLayout(new java.awt.FlowLayout());

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        irrelevant.setText("Here we are:");
        getContentPane().add(irrelevant);

        getContentPane().add(combobox);

        pack();
    }
    // </editor-fold>                        
    public static void main(String args[]) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                new Main().setVisible(true);
            }
        });
    }
    // Variables declaration - do not modify                     
    private javax.swing.JComboBox combobox;
    private javax.swing.JLabel irrelevant;
    // End of variables declaration                   
    private static final class MyRenderer extends DefaultTreeCellRenderer implements ListCellRenderer {
        public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
            setText((String) value);
            setFont(list.getFont());
            return this;
        }
        public void setFont(Font font) {
            if (RUN_CORRECTLY && font instanceof FontUIResource) {
                font = new Font(font.getName(), font.getStyle(), font.getSize());
            }
            super.setFont(font);
        }
    }
}
---%<---

Using Mustang b35, this class runs fine and shows a frame with a combo box with two items.

Using Mustang b36, if you set RUN_CORRECTLY to true, it behaves the same. Otherwise, it throws some exceptions and the window never opens:

---%<---
java version "1.6.0-ea"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.6.0-ea-b36)
Java HotSpot(TM) Client VM (build 1.6.0-ea-b36, mixed mode, sharing)

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
        at sun.font.FontManager.getFont2D(Native Method)
        at sun.font.FontDesignMetrics.initMatrixAndMetrics(FontDesignMetrics.java:330)
        at sun.font.FontDesignMetrics.<init>(FontDesignMetrics.java:323)
        at sun.font.FontDesignMetrics.getMetrics(FontDesignMetrics.java:271)
        at sun.font.FontDesignMetrics.getMetrics(FontDesignMetrics.java:232)
        at java.awt.Component.getFontMetrics(Component.java:2695)
        at javax.swing.JComponent.getFontMetrics(JComponent.java:1652)
        at javax.swing.plaf.basic.BasicLabelUI.getBaseline(BasicLabelUI.java:281)
        at javax.swing.JComponent.getBaseline(JComponent.java:2585)
        at javax.swing.plaf.basic.BasicComboBoxUI.getDisplaySize(BasicComboBoxUI.java:1289)
        at javax.swing.plaf.metal.MetalComboBoxUI.getMinimumSize(MetalComboBoxUI.java:302)
        at javax.swing.plaf.basic.BasicComboBoxUI.getPreferredSize(BasicComboBoxUI.java:863)
        at javax.swing.JComponent.getPreferredSize(JComponent.java:1687)
        at java.awt.FlowLayout.preferredLayoutSize(FlowLayout.java:401)
        at java.awt.Container.preferredSize(Container.java:1559)
        at java.awt.Container.getPreferredSize(Container.java:1544)
        at javax.swing.JComponent.getPreferredSize(JComponent.java:1689)
        at javax.swing.JRootPane$RootLayout.preferredLayoutSize(JRootPane.java:835)
        at java.awt.Container.preferredSize(Container.java:1559)
        at java.awt.Container.getPreferredSize(Container.java:1544)
        at javax.swing.JComponent.getPreferredSize(JComponent.java:1689)
        at java.awt.BorderLayout.preferredLayoutSize(BorderLayout.java:694)
        at java.awt.Container.preferredSize(Container.java:1559)
        at java.awt.Container.getPreferredSize(Container.java:1544)
        at java.awt.Window.pack(Window.java:493)
        at comboboxtest.Main.<init>(Main.java:18)
        at comboboxtest.Main$1.run(Main.java:39)
        at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
        at java.awt.EventQueue.dispatchEvent(EventQueue.java:589)
        at java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:247)
        at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:168)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:162)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:154)
        at java.awt.EventDispatchThread.run(EventDispatchThread.java:115)
Exception in thread "AWT-EventQueue-0" Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
        at sun.font.FontManager.getFont2D(Native Method)
        at sun.font.FontDesignMetrics.initMatrixAndMetrics(FontDesignMetrics.java:330)
        at sun.font.FontDesignMetrics.<init>(FontDesignMetrics.java:323)
        at sun.font.FontDesignMetrics.getMetrics(FontDesignMetrics.java:271)
        at sun.font.FontDesignMetrics.getMetrics(FontDesignMetrics.java:232)
        at java.awt.Component.getFontMetrics(Component.java:2695)
        at javax.swing.JComponent.getFontMetrics(JComponent.java:1652)
        at javax.swing.plaf.basic.BasicLabelUI.getBaseline(BasicLabelUI.java:281)
        at javax.swing.JComponent.getBaseline(JComponent.java:2585)
        at javax.swing.plaf.basic.BasicComboBoxUI.getDisplaySize(BasicComboBoxUI.java:1289)
        at javax.swing.plaf.metal.MetalComboBoxUI.getMinimumSize(MetalComboBoxUI.java:302)
        at javax.swing.plaf.basic.BasicComboBoxUI.getPreferredSize(BasicComboBoxUI.java:863)
        at javax.swing.JComponent.getPreferredSize(JComponent.java:1687)
        at java.awt.FlowLayout.layoutContainer(FlowLayout.java:586)
        at java.awt.Container.layout(Container.java:1402)
        at java.awt.Container.doLayout(Container.java:1391)
        at java.awt.Container.validateTree(Container.java:1474)
        at java.awt.Container.validateTree(Container.java:1481)
        at java.awt.Container.validateTree(Container.java:1481)
        at java.awt.Container.validateTree(Container.java:1481)
        at java.awt.Container.validate(Container.java:1449)
        at java.awt.Window.dispatchEventImpl(Window.java:1807)
        at java.awt.Component.dispatchEvent(Component.java:4194)
        at java.awt.EventQueue.dispatchEvent(EventQueue.java:591)
        at java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:247)
        at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:168)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:162)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:154)
        at java.awt.EventDispatchThread.run(EventDispatchThread.java:115)
java.lang.NullPointerException
        at sun.font.FontManager.getFont2D(Native Method)
        at sun.font.FontDesignMetrics.initMatrixAndMetrics(FontDesignMetrics.java:330)
        at sun.font.FontDesignMetrics.<init>(FontDesignMetrics.java:323)
        at sun.font.FontDesignMetrics.getMetrics(FontDesignMetrics.java:271)
        at sun.font.FontDesignMetrics.getMetrics(FontDesignMetrics.java:232)
        at java.awt.Component.getFontMetrics(Component.java:2695)
        at javax.swing.JComponent.getFontMetrics(JComponent.java:1652)
        at javax.swing.plaf.basic.BasicLabelUI.getBaseline(BasicLabelUI.java:281)
        at javax.swing.JComponent.getBaseline(JComponent.java:2585)
        at javax.swing.plaf.basic.BasicComboBoxUI.getDisplaySize(BasicComboBoxUI.java:1289)
        at javax.swing.plaf.metal.MetalComboBoxUI.getMinimumSize(MetalComboBoxUI.java:302)
        at javax.swing.plaf.basic.BasicComboBoxUI.getPreferredSize(BasicComboBoxUI.java:863)
        at javax.swing.JComponent.getPreferredSize(JComponent.java:1687)
        at java.awt.FlowLayout.layoutContainer(FlowLayout.java:586)
        at java.awt.Container.layout(Container.java:1402)
        at java.awt.Container.doLayout(Container.java:1391)
        at java.awt.Container.validateTree(Container.java:1474)
        at java.awt.Container.validateTree(Container.java:1481)
        at java.awt.Container.validateTree(Container.java:1481)
        at java.awt.Container.validateTree(Container.java:1481)
        at java.awt.Container.validate(Container.java:1449)
        at java.awt.Window.dispatchEventImpl(Window.java:1807)
        at java.awt.Component.dispatchEvent(Component.java:4194)
        at java.awt.EventQueue.dispatchEvent(EventQueue.java:591)
        at java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:247)
        at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:168)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:162)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:154)
        at java.awt.EventDispatchThread.run(EventDispatchThread.java:115)
---%<---
###@###.### 2005-05-18 23:01:55 GMT

Comments
SUGGESTED FIX One fix might be to improve BasicLabelUI.getBaseline to handle the case that the label has a null font. This would be most likely to be compatible for 3rd-party code, since this is a newly introduced method. I'm not sure how this would work, however. Another fix might be to state that the label may not have a null font under such conditions (i.e. in use as a cell renderer on a valid component), and fix DefaultTreeCellRenderer to not override getFont or setFont, but instead call setFont(tree.getFont()) in getTreeCellRendererComponent. This would be easy enough and make it behave analogously to DefaultListCellRenderer, so it might be the best fix. Anyone unfortunate enough to have copied and modified (not subclassed) the DTCR impl for a custom list cell renderer would still have a problem, of course, but this is probably rare. ###@###.### 2005-05-18 23:01:55 GMT
18-05-2005

WORK AROUND Workaround can be seen in demo class: disable the trick in DefaultTreeCellRenderer.setFont by overriding it to never pass a FontUIResource to super. Seems to work successfully in dev versions of the NB IDE. ###@###.### 2005-05-18 23:01:55 GMT
18-05-2005

EVALUATION I did some investigation into the stack trace. If you follow the logic backwards from the actual NPE, you can see that BasicLabelUI.getBaseline is calling JLabel.getFontMetrics with a null argument, thus JLabel.getFont() is returning null. This is of course unusual - since Component.getFont() traverses the hierarchy tree - but apparently not forbidden in all cases. (For example, in the very same class, BasicLabelUI.getPreferredSize calls label.getFont() and then explicitly handles the case that it is null.) The particular case in which this was observed (NB IDE) involved a JComboBox whose cell renderer was set to a subclass of DefaultTreeCellRenderer. DTCR has special behavior for getFont and setFont; it uses null as a value to mean "inherit font from the JTree". Now if setFont is called at least once (e.g. during UI delegate initialization) with a FontUIResource (which is the normal case), Component.setFont(null) is called, and DTCR.getFont() will return non-null only *after* the 'tree' field is set - which will not happen until after the first attempted usage of the DTCR instance as a tree renderer. Fine so far. In NB however, the DTCR renderer subclass was used as a list cell renderer too (to share code between tree and list renderers). This had the unintentional effect of making getFont return null when the renderer was used on a list (since it had never served as a tree renderer). In earlier JDKs, this was apparently harmless - e.g. BasicLabelUI.paint calls SwingUtilities2.getFontMetrics, which sensibly defaults the font for the component (if not set to non-null) from the Graphics. In Mustang b36, however, the new Baseline API does not deal well with the null font property, as can be seen from the stack trace. BLUI.getBaseline makes no attempt to correct for a null label.getFont(). ###@###.### 2005-05-18 23:01:55 GMT This is embarrasing bug has been fixed as 6272022. BasicLabelUI.getBaseline now promptly bails if the font is null. ###@###.### 2005-05-26 20:46:36 GMT
18-05-2005