JDK-6346886 : Windows XP PLAF Table Header with TableSorter.setFont() = NullPointerException
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 5.0
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2005-11-07
  • Updated: 2010-04-02
  • Resolved: 2006-07-31
Related Reports
Duplicate :  
Relates :  
Description
FULL PRODUCT VERSION :
  Bug exists on both

java version "1.5.0_05"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_05-b05)
Java HotSpot(TM) Client VM (build 1.5.0_05-b05, mixed mode)

and

java version "1.4.2_01"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2_01-b06)
Java HotSpot(TM) Client VM (build 1.4.2_01-b06, mixed mode)


ADDITIONAL OS VERSION INFORMATION :
Windows XP

A DESCRIPTION OF THE PROBLEM :
A nullpointer bug exists when trying to use the setFont function on a JTable that has been created using TableSorter and is viewed in Windows XP.

It may be possible to argue that the bug exists in WindowsTableHeaderUI
or in TableSorter.

TableSorter is not by default included in any JDK release but it is written by sun and made available from
http://java.sun.com/docs/books/tutorial/uiswing/components/table.html

This bug is probably related to Bug ID 5049957 but it is not the same.


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile and run the attached code
Click the Test Button

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Table font size should increase
ACTUAL -
NullPointerException thrown



ERROR MESSAGES/STACK TRACES THAT OCCUR :
java.lang.NullPointerException
	at com.sun.java.swing.plaf.windows.WindowsTableHeaderUI$XPDefaultRenderer.paint(WindowsTableHeaderUI.java:81)
	at javax.swing.CellRendererPane.paintComponent(CellRendererPane.java:134)
	at javax.swing.plaf.basic.BasicTableHeaderUI.paintCell(BasicTableHeaderUI.java:401)
	at javax.swing.plaf.basic.BasicTableHeaderUI.paint(BasicTableHeaderUI.java:341)
	at javax.swing.plaf.ComponentUI.update(ComponentUI.java:142)
	at javax.swing.JComponent.paintComponent(JComponent.java:541)
	at javax.swing.JComponent.paint(JComponent.java:808)
	at javax.swing.JComponent.paintChildren(JComponent.java:647)
	at javax.swing.JComponent.paint(JComponent.java:817)
	at javax.swing.JViewport.paint(JViewport.java:722)
	at javax.swing.JComponent.paintChildren(JComponent.java:647)
	at javax.swing.JComponent.paint(JComponent.java:817)
	at javax.swing.JComponent.paintChildren(JComponent.java:647)
	at javax.swing.JComponent.paint(JComponent.java:817)
	at javax.swing.JComponent.paintChildren(JComponent.java:647)
	at javax.swing.JComponent.paint(JComponent.java:817)
	at javax.swing.JComponent.paintChildren(JComponent.java:647)
	at javax.swing.JComponent.paint(JComponent.java:817)
	at javax.swing.JLayeredPane.paint(JLayeredPane.java:557)
	at javax.swing.JComponent.paintChildren(JComponent.java:647)
	at javax.swing.JComponent.paintWithOffscreenBuffer(JComponent.java:4802)
	at javax.swing.JComponent.paintDoubleBuffered(JComponent.java:4748)
	at javax.swing.JComponent.paint(JComponent.java:798)
	at java.awt.GraphicsCallback$PaintCallback.run(GraphicsCallback.java:21)
	at sun.awt.SunGraphicsCallback.runOneComponent(SunGraphicsCallback.java:60)
	at sun.awt.SunGraphicsCallback.runComponents(SunGraphicsCallback.java:97)
	at java.awt.Container.paint(Container.java:1312)
	at javax.swing.JFrame.update(JFrame.java:392)
	at sun.awt.RepaintArea.paint(RepaintArea.java:169)
	at sun.awt.windows.WComponentPeer.handleEvent(WComponentPeer.java:260)
	at java.awt.Component.dispatchEventImpl(Component.java:3678)
	at java.awt.Container.dispatchEventImpl(Container.java:1627)
	at java.awt.Window.dispatchEventImpl(Window.java:1606)
	at java.awt.Component.dispatchEvent(Component.java:3477)
	at java.awt.EventQueue.dispatchEvent(EventQueue.java:456)
	at java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:201)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:151)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:145)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:137)
	at java.awt.EventDispatchThread.run(EventDispatchThread.java:100)


REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import javax.swing.*;
import javax.swing.table.AbstractTableModel;

import TableSorter;

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

/*
 * Created on 24-Mar-2004
 *
 */

/**
 * @author Rob Jones
 */
public class TableFontSizeTest extends JFrame implements ActionListener
{
    JLabel simple;
    JTable table;
	TableSorter sorter;
	
    public TableFontSizeTest()
    {
        //Must be run on WindowsXP for bug to show up.
        setLAF(UIManager.getSystemLookAndFeelClassName());
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        simple = new JLabel("Simple");
        
        MyTableModel tableModel = new MyTableModel();
        sorter = new TableSorter(tableModel);
        table = new JTable(sorter);
        sorter.setTableHeader(table.getTableHeader());
        
        JScrollPane tableScrollPanel = new JScrollPane();
        tableScrollPanel.add(table);
        tableScrollPanel.setViewportView(table);
        tableScrollPanel.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
        tableScrollPanel.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
        tableScrollPanel.setBorder(BorderFactory.createLoweredBevelBorder());
        
        JButton button = new JButton("Test");
        button.addActionListener(this);
        JPanel contents = new JPanel();
        contents.add(simple);
        contents.add(tableScrollPanel);
        contents.add(button);
        getContentPane().add(contents);
        pack();
    }

    public void actionPerformed(ActionEvent e)
    {
        table.setFont(new Font("Arial",Font.BOLD,14));
        table.getTableHeader().setFont(new Font("Arial",Font.BOLD,14));
        SwingUtilities.updateComponentTreeUI(this);
    }

    public static void main(String args[])
    {
        TableFontSizeTest test = new TableFontSizeTest();
        test.setVisible(true);
    }
    
    
    private static void setLAF(String lafName)
    {
        try
        {
            UIManager.setLookAndFeel(lafName);
        }
        catch (ClassNotFoundException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        catch (InstantiationException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        catch (IllegalAccessException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        catch (UnsupportedLookAndFeelException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    
    //This MyTableModel class taken from Swing Tutorial
   class MyTableModel extends AbstractTableModel
   {
        final String[] columnNames = {"First Name",
                                      "Last Name",
                                      "Sport",
                                      "# of Years",
                                      "Vegetarian"};
        final Object[][] data = {
            {"Mary", "Campione",
             "Snowboarding", new Integer(5), new Boolean(false)},
            {"Alison", "Huml",
             "Rowing", new Integer(3), new Boolean(true)},
            {"Kathy", "Walrath",
             "Chasing toddlers", new Integer(2), new Boolean(false)},
            {"Mark", "Andrews",
             "Speed reading", new Integer(20), new Boolean(true)},
            {"Angela", "Lih",
             "Teaching high school", new Integer(4), new Boolean(false)}
        };

        public int getColumnCount() {
            return columnNames.length;
        }
        
        public int getRowCount() {
            return data.length;
        }

        public String getColumnName(int col) {
            return columnNames[col];
        }

        public Object getValueAt(int row, int col) {
            return data[row][col];
        }

        /*
         * JTable uses this method to determine the default renderer/
         * editor for each cell.  If we didn't implement this method,
         * then the last column would contain text ("true"/"false"),
         * rather than a check box.
         */
        public Class getColumnClass(int c) {
            return getValueAt(0, c).getClass();
        }

        /*
         * Don't need to implement this method unless your table's
         * editable.
         */
        public boolean isCellEditable(int row, int col) {
            //Note that the data/cell address is constant,
            //no matter where the cell appears onscreen.
            if (col < 2) {
                return false;
            } else {
                return true;
            }
        }

        /*
         * Don't need to implement this method unless your table's
         * data can change.
         */
        public void setValueAt(Object value, int row, int col) {
            if (false) {
                System.out.println("Setting value at " + row + "," + col
                                   + " to " + value
                                   + " (an instance of "
                                   + value.getClass() + ")");
            }

            if (data[0][col] instanceof Integer
                    && !(value instanceof Integer)) {
                //With JFC/Swing 1.1 and JDK 1.2, we need to create
                //an Integer from the value; otherwise, the column
                //switches to contain Strings.  Starting with v 1.3,
                //the table automatically converts value to an Integer,
                //so you only need the code in the 'else' part of this
                //'if' block.
                //XXX: See TableEditDemo.java for a better solution!!!
                try {
                    data[row][col] = new Integer(value.toString());
                    fireTableCellUpdated(row, col);
                } catch (NumberFormatException e) {
                    System.out.println("NFE");
                }
            } else {
                data[row][col] = value;
                fireTableCellUpdated(row, col);
            }

        }
    }
    
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
A kinda of workaround exists by modifying TableSorter
Commenting out two lines in setTableHeader in TableSorter as per below
This stops the custom header which paints the arrow from being installed.

    public void setTableHeader(JTableHeader tableHeader) {
        if (this.tableHeader != null) {
            this.tableHeader.removeMouseListener(mouseListener);
            TableCellRenderer defaultRenderer = this.tableHeader.getDefaultRenderer();
            if (defaultRenderer instanceof SortableHeaderRenderer) {
                this.tableHeader.setDefaultRenderer(((SortableHeaderRenderer) defaultRenderer).tableCellRenderer);
            }
        }
        this.tableHeader = tableHeader;
        if (this.tableHeader != null) {
            this.tableHeader.addMouseListener(mouseListener);
//            this.tableHeader.setDefaultRenderer(
//                    new SortableHeaderRenderer(this.tableHeader.getDefaultRenderer()));
        }
    }

Comments
EVALUATION It's actually not the setFont call that causes the problem. It's the call to: SwingUtilities.updateComponentTreeUI(this); Incidentally, this call isn't needed to get the desired effect in this application. A work-around would be to remove it. In any case, the problem results from wrapping the renderer provided by the look and feel, which becomes invalid when the UI is updated. This is a duplicate of 6429812.
31-07-2006

EVALUATION I've moved this to Dolphin because it only happens with TableSorter, which is external code that the user can change.
06-12-2005

EVALUATION The stack trace appears similar to 5049957. Could be a duplicate.
07-11-2005