coderazzi / tablefilter-swing

TableFilter is a set of Swing components to support user-driven filtering on table.
https://coderazzi.net/tablefilter
MIT License
7 stars 2 forks source link

Exception raised when updating table model #24

Closed coderazzi closed 12 years ago

coderazzi commented 12 years ago

Original report by altmany (Bitbucket: altmany, GitHub: altmany).


What steps will reproduce the problem?
1. set auto-choice filter on a table with no data
2. now modify the table model to have some data rows

Resulting exception:
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
    at java.util.ArrayList.RangeCheck(Unknown Source)
    at java.util.ArrayList.get(Unknown Source)
    at net.coderazzi.filters.gui.AdaptiveChoicesHandler$AdaptiveChoicesSupport.include(AdaptiveChoicesHandler.java:491)
    at javax.swing.DefaultRowSorter.include(Unknown Source)
    at javax.swing.DefaultRowSorter.initializeFilteredMapping(Unknown Source)
    at javax.swing.DefaultRowSorter.sort(Unknown Source)
    at javax.swing.DefaultRowSorter.allChanged(Unknown Source)
    at javax.swing.DefaultRowSorter.modelStructureChanged(Unknown Source)
    at javax.swing.JTable.tableChanged(Unknown Source)

What version of the product are you using? On what operating system?
tablefilter 4.3.0; 1.6.0_17-b04 64-Bit Server VM mixed mode; Win7 SP1

Workaround that I found (no exception raised, things look ok):
filter.setEnabled(false);
updateTableModel()
filter.setEnabled(true);
filter.resetFilter();

Original issue reported on code.google.com by altmany on 2012-02-20 20:12:46

coderazzi commented 12 years ago

Original comment by coderazzi coderazzi (Bitbucket: coderazzi, GitHub: coderazzi).


Hi, Yair, I have a simple test case for this scenario, but I am not able to raise the
exception, could you send a simple example of the code that leads to the exception?

This is the code I am using as test case:

package test;

import net.coderazzi.filters.gui.AutoChoices;
import net.coderazzi.filters.gui.TableFilterHeader;

import java.awt.BorderLayout;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.table.DefaultTableModel;

public class Main {

    public static void main(String[] args) throws Exception {
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        JFrame jf = new JFrame("Test");
        jf.setSize(200, 100);
        final DefaultTableModel model = new DefaultTableModel(0, 1);
        JTable table = new JTable(model);
        TableFilterHeader header = new TableFilterHeader(table);
        header.setAutoChoices(AutoChoices.ENABLED);
        // add the data on a separate thread. After 5 seconds the table should be
        // already displayed (lousy test case).
        new Thread() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(5000);
                    } catch (Exception ex) {
                    }
                    SwingUtilities.invokeLater(new Runnable() {
                            @Override
                            public void run() {
                                model.addRow(new String[] { "hello" });
                            }
                        });
                }
            }.start();
        jf.getContentPane().add(new JScrollPane(table), BorderLayout.NORTH);
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jf.setVisible(true);
    }
}

I have tried it using the separate thread and without deferred execution, but still
no exception (on a XP SP3 machine with latest Java 1.6.0_29)

The same for this different test case, where the model is attached after the filter
header:

package test;

import net.coderazzi.filters.gui.AutoChoices;
import net.coderazzi.filters.gui.TableFilterHeader;

import java.awt.BorderLayout;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.table.DefaultTableModel;

public class Main2 {

    public static void main(String[] args) throws Exception {
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        JFrame jf = new JFrame("Test");
        jf.setSize(200, 100);
        final JTable table = new JTable();
        TableFilterHeader header = new TableFilterHeader(table);
        header.setAutoChoices(AutoChoices.ENABLED);

        // add the data on a separate thread. After 5 seconds the table should be
        // already displayed (lousy test case).
        new Thread() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(5000);
                    } catch (Exception ex) {
                    }
                    SwingUtilities.invokeLater(new Runnable() {
                            @Override
                            public void run() {
                                DefaultTableModel model = new DefaultTableModel(0,
1);
                                table.setModel(model);
                                model.addRow(new String[] { "hello" });
                            }
                        });
                }
            }.start();
        jf.getContentPane().add(new JScrollPane(table), BorderLayout.NORTH);
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jf.setVisible(true);
    }

}

Best regards

Original issue reported on code.google.com by coderazzi on 2012-02-22 12:36:21

coderazzi commented 12 years ago

Original comment by altmany (Bitbucket: altmany, GitHub: altmany).


Hi Luis,

It is difficult for me to say, since I am running the tablefilter on JTables created
via Matlab, not directly in Java. I believe that the situation is similar to your first
test case, but instead of using model.addRow() on the existing row, replace the underlying
table's model with a new model that has some rows.

In any case, even it the exact situation can't be reproduced in pure Java, perhaps
the stack trace can be enough to diagnose the issue at AdaptiveChoicesHandler.java
line 491 and fix/bypass it. Maybe the workaround I mentioned could be directly integrated.

Not sure if all this helps, but let's hope... At least there's a workaround

Original issue reported on code.google.com by altmany on 2012-02-22 13:32:04

coderazzi commented 12 years ago

Original comment by altmany (Bitbucket: altmany, GitHub: altmany).


Of course I meant to say: "...instead of using model.addRow() on the exiting model (not
row), replace..."

Original issue reported on code.google.com by altmany on 2012-02-22 13:33:28

coderazzi commented 12 years ago

Original comment by coderazzi coderazzi (Bitbucket: coderazzi, GitHub: coderazzi).


Hi, Yair, your workaround is in fact the way the tablefilter works internally: when
it detects a model change, it disables the filters, recreating them afterwards.

Is the stack trace complete? I would need to see what is originating the tableChanged
call: it can be a model change, but also some specific calls to table model events.

I went through the different possibilities on a model change, but the code seems to
be fine; the another alternative, of model events is more extensive, hereby it could
be very helpful having the full stack trace. 

It could be that some event is wrongly fired, or that I haven't considered some specific
scenario. Perhaps I could build a specific tablefilter library with additional logging?

Cheers!

Original issue reported on code.google.com by coderazzi on 2012-02-22 16:30:37

coderazzi commented 12 years ago

Original comment by altmany (Bitbucket: altmany, GitHub: altmany).


Here's the full stack trace - HTH:

java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
    at java.util.ArrayList.RangeCheck(Unknown Source)
    at java.util.ArrayList.get(Unknown Source)
    at net.coderazzi.filters.gui.AdaptiveChoicesHandler$AdaptiveChoicesSupport.include(AdaptiveChoicesHandler.java:491)
    at javax.swing.DefaultRowSorter.include(Unknown Source)
    at javax.swing.DefaultRowSorter.initializeFilteredMapping(Unknown Source)
    at javax.swing.DefaultRowSorter.sort(Unknown Source)
    at javax.swing.DefaultRowSorter.allChanged(Unknown Source)
    at javax.swing.DefaultRowSorter.modelStructureChanged(Unknown Source)
    at javax.swing.JTable.tableChanged(Unknown Source)
    at com.jidesoft.grid.JideTable.tableChanged(Unknown Source)
    at com.jidesoft.grid.CellSpanTable.tableChanged(Unknown Source)
    at com.mathworks.hg.peer.ui.table.UISortableTable.tableChanged(UISortableTable.java:192)
    at javax.swing.table.AbstractTableModel.fireTableChanged(Unknown Source)
    at javax.swing.table.AbstractTableModel.fireTableStructureChanged(Unknown Source)
    at com.mathworks.hg.peer.ui.table.DefaultUIStyleTableModel.updateData(DefaultUIStyleTableModel.java:370)
    at com.mathworks.hg.peer.ui.UITablePeer.doDataChanged(UITablePeer.java:603)
    at com.mathworks.hg.peer.ui.UITablePeer$14.run(UITablePeer.java:583)
    at com.mathworks.hg.util.HGPeerQueue$HGPeerRunnablesRunner.runit(HGPeerQueue.java:228)
    at com.mathworks.hg.util.HGPeerQueue$HGPeerRunnablesRunner.runNotThese(HGPeerQueue.java:260)
    at com.mathworks.hg.util.HGPeerQueue$HGPeerRunnablesRunner.run(HGPeerQueue.java:276)
    at java.awt.event.InvocationEvent.dispatch(Unknown Source)
    at java.awt.EventQueue.dispatchEvent(Unknown Source)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.run(Unknown Source)

Matlab has a very convoluted way of updating the underlying JTable. From what I see
in the code, it first creates new table rows having empty (null) cell values, and then
populates the cells with Objects. Each cell gets a different Object - this is weird
and very inefficient in so many ways, but this is how it is. 

If I use a newer Matlab release then no exception is raised, so Matlab probably fixed
the EDT timing issue, or whatever it was that caused the problem. I just thought that
in any case you may wish to perhaps put some sanity checks in AdaptiveChoicesHandler.java:491,
just in case other users of older Matlabs, or users in other environments, encounter
a similar situation.

Original issue reported on code.google.com by altmany on 2012-02-23 11:14:02

coderazzi commented 12 years ago

Original comment by coderazzi coderazzi (Bitbucket: coderazzi, GitHub: coderazzi).


Hi, Yair, 

well, thanks a lot for getting deeper into the problem and checking the newer Matlab
release;

I have been checking different ways to get the same exception, by firing wrong events
related to table changes, and, in fact, I got some nice stack traces, but directly
on the TableSorter class -which belongs to the Java SDK-. So I am not that sure of
how much it will be accomplished with this patch. 

Anyway, there is no harm in implementing it (and performance wise is perfectly fine),
so I will include it on the next release. If you would rather have a fast release to
solve this issue on older Matlab releases, please let me know it, and I will produce
the release this week.

Best regards,

  Lu.

Original issue reported on code.google.com by coderazzi on 2012-02-28 11:31:02

coderazzi commented 12 years ago

Original comment by coderazzi coderazzi (Bitbucket: coderazzi, GitHub: coderazzi).


The proposed change is implemented on version 4.4.0

Original issue reported on code.google.com by coderazzi on 2012-08-22 22:31:40