3

I have a JTable which, when the appropriate button is clicked, begins to fill with the results of a file tree walk that goes on in the background. This works fine.

I then decided I want the table to be sorted. After some reading I created a TableRowSorter and set the table to use it. It appeared to work, but upon closer inspection I noticed several of the file results were absent. I disabled the sorter and ran the program again and all of the files were present, upon re-enabling the sorter again some were missed, but it seemed to be different files each time that were being dropped.

To examine this I created a self-contained block of code as a test (see below), which was to be representative of the JTable code (In fact, large chunks were lifted directly from the existing program code). The file tree walk is represented by a for loop. Again, it worked perfectly without the sorter. When I enabled the sorter, however, (by uncommenting line 29) the entire program frozeand I was told there was a NullPointerException.

I have no Idea what is causing either of these problems, nor if they are, in fact, even related. Any ideas on what is wrong are welcome.

import javax.swing.table.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;

public class Sort extends JFrame{

    private JTable table;
    private DefaultTableModel model;
    private TableRowSorter<DefaultTableModel> sorter;

    private JButton go;

    public Sort(){
        super("Sort");

        // Create table and model
        model = new DefaultTableModel(0, 4);
        table = new JTable(model);

        // Setup sorting
        sorter = new TableRowSorter<DefaultTableModel>(model);
        ArrayList<RowSorter.SortKey> sortKeys = new ArrayList<RowSorter.SortKey>();
        sortKeys.add(new RowSorter.SortKey(2, SortOrder.ASCENDING));
        sortKeys.add(new RowSorter.SortKey(3, SortOrder.ASCENDING));
        sortKeys.add(new RowSorter.SortKey(0, SortOrder.ASCENDING));
        sorter.setSortKeys(sortKeys); 
        //table.setRowSorter(sorter);

        // Create Scroll Pane
        JScrollPane tableScroller = new JScrollPane(table);
        table.setFillsViewportHeight(true);
        tableScroller.setPreferredSize(new Dimension(500, 200));

        // Setup button
        go = new JButton("Go");
        go.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e){
                SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>(){
                    public Void doInBackground(){
                        for(int i = 0; i < 200; i++){
                            model.addRow( new Object[] { (new Integer(i)), String.valueOf(i), String.valueOf(i%50), String.valueOf(i%10) } );
                        }
                        return null;
                    }
                };
                worker.execute();
            }
        });

        // Assemble GUI
        JPanel panel = new JPanel(new BorderLayout());
        panel.add(tableScroller, BorderLayout.CENTER);
        panel.add(go, BorderLayout.SOUTH);

        setContentPane(panel);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        pack();
        setVisible(true);

    }

    public static void main(String[] args){
        SwingUtilities.invokeLater(new Runnable(){
            public void run(){
                new Sort();
            }
        });
    }

}

Stacktrace

This is part of the stack trace, it repeats..

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
    at javax.swing.DefaultRowSorter.convertRowIndexToModel(DefaultRowSorter.java:518)
    at javax.swing.JTable.convertRowIndexToModel(JTable.java:2645)
    at javax.swing.JTable.getValueAt(JTable.java:2720)
    at javax.swing.JTable.prepareRenderer(JTable.java:5718)
    at javax.swing.plaf.basic.BasicTableUI.paintCell(BasicTableUI.java:2114)
    at javax.swing.plaf.basic.BasicTableUI.paintCells(BasicTableUI.java:2016)
    at javax.swing.plaf.basic.BasicTableUI.paint(BasicTableUI.java:1812)
    at javax.swing.plaf.ComponentUI.update(ComponentUI.java:161)
    at javax.swing.JComponent.paintComponent(JComponent.java:778)
    at javax.swing.JComponent.paint(JComponent.java:1054)
    at javax.swing.JComponent.paintToOffscreen(JComponent.java:5221)
    at javax.swing.BufferStrategyPaintManager.paint(BufferStrategyPaintManager.java:295)
    at javax.swing.RepaintManager.paint(RepaintManager.java:1206)
    at javax.swing.JComponent._paintImmediately(JComponent.java:5169)
    at javax.swing.JComponent.paintImmediately(JComponent.java:4980)
    at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:770)
    at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:728)
    at javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:677)
    at javax.swing.RepaintManager.access$700(RepaintManager.java:59)
    at javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1621)
    at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:251)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:705)
    at java.awt.EventQueue.access$000(EventQueue.java:101)
    at java.awt.EventQueue$3.run(EventQueue.java:666)
    at java.awt.EventQueue$3.run(EventQueue.java:664)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:675)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:211)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:128)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:117)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:113)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:105)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
    ...
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
James
  • 2,483
  • 2
  • 24
  • 31

1 Answers1

10

Your code has a big problem that may explain what you observe: you are modifying your table model outside the EDT, which is evil.

Since you are using a SwingWorker, you should try to use it completely correctly, e.g. use its publish API to call model.addRow().

jfpoilpret
  • 10,449
  • 2
  • 28
  • 32
  • Can you clarify how one would use the publish api where the time consuming task is a file tree walk using nio? ( `Files.walkFileTree`) – James Sep 26 '11 at 14:25
  • Never mind, worked it out - I can put the file tree walk results into a Vector and then copy them from that to the table using publish I think... – James Sep 26 '11 at 14:35
  • 2
    +1 for the "evil" comment :). Based on your posed example you can publish the row Array and then invoke the model addRow() method from within the process() method of the SwingWorker. See [Tasks that Have Interim Results](http://download.oracle.com/javase/tutorial/uiswing/concurrency/interim.html). – camickr Sep 26 '11 at 14:38
  • Correct use of SwingWorker has fixed both issues (and another issue I had put on hold as it was just irritating it wasn't causing a crash). Thank you for your help, I mistakenly thought that SwingWorkers were safe to access the model (which I now realise they are not). – James Sep 26 '11 at 19:39
  • +1 for a nearly 3.5-year old post reminding me I need to pay closer attention to where I put my table updating code, lol. I had an `addRow()` statement in a for-loop in a separate thread but forgot to encase it in `SwingUtilities.invokeLater()` to run it on the EDT. Couldn't, for the life of me, figure out what was causing the error until I gave up and searched for it, lol. – DGolberg Feb 06 '15 at 21:43