Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FlatLaf causes ArrayIndexOutOfBoundsException ONLY on Linux #532

Closed
derreisende77 opened this issue May 9, 2022 · 13 comments
Closed

FlatLaf causes ArrayIndexOutOfBoundsException ONLY on Linux #532

derreisende77 opened this issue May 9, 2022 · 13 comments
Milestone

Comments

@derreisende77
Copy link

I tried migrating my existing Java app to FlatLaf. It works perfect on macOS and Windows, but I can repeatedly produce the following exception on various Linux versions with X11 or Wayland:

Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: Index -1 out of bounds for length 20
	at java.base/java.util.Vector.elementData(Vector.java:731)
	at java.base/java.util.Vector.elementAt(Vector.java:469)
	at java.desktop/javax.swing.table.DefaultTableColumnModel.getColumn(DefaultTableColumnModel.java:295)
	at java.desktop/javax.swing.plaf.basic.BasicTableHeaderUI.getHeaderRenderer(BasicTableHeaderUI.java:724)
	at java.desktop/javax.swing.plaf.basic.BasicTableHeaderUI.paintCell(BasicTableHeaderUI.java:740)
	at java.desktop/javax.swing.plaf.basic.BasicTableHeaderUI.paint(BasicTableHeaderUI.java:716)
	at com.formdev.flatlaf.ui.FlatTableHeaderUI.paint(FlatTableHeaderUI.java:234)
	at java.desktop/javax.swing.plaf.ComponentUI.update(ComponentUI.java:161)
	at java.desktop/javax.swing.JComponent.paintComponent(JComponent.java:852)
	at java.desktop/javax.swing.JComponent.paint(JComponent.java:1128)
	at java.desktop/javax.swing.JComponent.paintChildren(JComponent.java:961)
	at java.desktop/javax.swing.JComponent.paint(JComponent.java:1137)
	at java.desktop/javax.swing.JViewport.paint(JViewport.java:736)
	at java.desktop/javax.swing.JComponent.paintChildren(JComponent.java:961)
	at java.desktop/javax.swing.JComponent.paint(JComponent.java:1137)
	at java.desktop/javax.swing.JComponent.paintToOffscreen(JComponent.java:5318)
	at java.desktop/javax.swing.RepaintManager$PaintManager.paintDoubleBufferedImpl(RepaintManager.java:1656)
	at java.desktop/javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(RepaintManager.java:1631)
	at java.desktop/javax.swing.RepaintManager$PaintManager.paint(RepaintManager.java:1569)
	at java.desktop/javax.swing.RepaintManager.paint(RepaintManager.java:1336)
	at java.desktop/javax.swing.JComponent._paintImmediately(JComponent.java:5266)
	at java.desktop/javax.swing.JComponent.paintImmediately(JComponent.java:5076)
	at java.desktop/javax.swing.RepaintManager$4.run(RepaintManager.java:878)
	at java.desktop/javax.swing.RepaintManager$4.run(RepaintManager.java:861)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
	at java.desktop/javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:861)
	at java.desktop/javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:834)
	at java.desktop/javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:784)
	at java.desktop/javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1897)
	at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:318)
	at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:773)
	at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:720)
	at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:714)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
	at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:742)
	at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
	at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)

I cannot reproduce this error both with Metal or Nimbus L&F therefore I guess it is caused somewhere within FlatLaf.

I tested FlatLaf versions 2.2,2.1,2.0 and all produce the exception using Java 17 on Linux.

@DevCharly
Copy link
Collaborator

The exception occurs when you drag a column, right?

Could you please set a breakpoint at following line
and try to find you why viewIndexForColumn(draggedColumn) returns -1?

@derreisende77
Copy link
Author

derreisende77 commented May 11, 2022

Actually I am not dragging. The exception occurs when a popup menu is shown over the table headers and I trigger an update to the table model. Our code is really old and deletes and creates the columns during this process on the fly. It might rearrange columns during that process as well. That might be the reason why the index might become -1 during the process. But as I have written this exception only occurs on linux. I will try debugging tonight.

@derreisende77
Copy link
Author

@DevCharly So I finally managed to debug on macOS, Linux and Windows.
Using the exact command sequence on all OS triggers the breakpoint at L701 only on Linux. macOS and Windows never reach the breakpoint.
viewIndexForColumn returns -1 on Linux because draggedColumn points to an (old now) invalid TableColumn object which no longer exists in the column model.

@derreisende77
Copy link
Author

derreisende77 commented May 31, 2022

Using Nimbus LaF draggedColumnIndex receives a valid index.

@DevCharly
Copy link
Collaborator

draggedColumn is set when pressing mouse on column (in BasicTableHeaderUI.MouseInputHandler.mousePressed()) and changed to null when the mouse is released (in BasicTableHeaderUI.MouseInputHandler.mouseReleased()).

I've following theory: If you show a popup menu on column click, then the table header probably not receives the mouse-released event, and draggedColumn is not changed to null. So if the popup menu action removes the clicked column, draggedColumn still contains the already removed column, which later causes the exception.

But don't understand that it happens only on Linux. Any why not on Windows and macOS or in Nimbus?

Will try to reproduce this.

BTW How do you show the popup menu? Do you use a mouse listener? On pressed or on released?
Or simply table.getTableHeader().setComponentPopupMenu( popupMenu );?

@DevCharly
Copy link
Collaborator

Ok, the theory from my previous post is correct.
Found a way to reproduce the exception.

Occurs in any look and feel and probably in any Java version (tried 8 and 18),
if the table structure is changed from a table header context menu action.

Run following app, right-click on table header and select the "fireTableStructureChanged" menu item immediately throws the exception.

import javax.swing.*;
import javax.swing.table.AbstractTableModel;
import com.formdev.flatlaf.FlatLightLaf;

public class TableHeaderTest
{
    public static void main( String[] args ) {
        SwingUtilities.invokeLater( () -> {
            FlatLightLaf.setup();

            JTable table = new JTable( new Object[][] {
                { "abc", "def" }
            }, new Object[] { "Col A", "Col B" } );

            JMenuItem mi = new JMenuItem( "fireTableStructureChanged" );
            mi.addActionListener( e -> {
                ((AbstractTableModel)table.getModel()).fireTableStructureChanged();
            } );

            JPopupMenu pm = new JPopupMenu();
            pm.add( mi );
            table.getTableHeader().setComponentPopupMenu( pm );

            JOptionPane.showMessageDialog( null, new JScrollPane( table ) );
        } );
    }
}

DevCharly added a commit that referenced this issue May 31, 2022
…ving column) from a table header popup menu action (issue #532)
@DevCharly
Copy link
Collaborator

I've added a workaround to avoid the exception. See commit 6c18431
Try latest 2.4-SNAPSHOT from https://github.com/JFormDesigner/FlatLaf#snapshots

If you support other Lafs (e.g. Nimbus) in your application, then you should consider to clear dragged column in those actions that change table structure from table header popup menu. E.g.

table1.getTableHeader().setDraggedColumn( null );

@DevCharly DevCharly added this to the 2.4 milestone May 31, 2022
@derreisende77
Copy link
Author

derreisende77 commented May 31, 2022

BTW How do you show the popup menu? Do you use a mouse listener? On pressed or on released? Or simply table.getTableHeader().setComponentPopupMenu( popupMenu );?
Yes I am using a mouse listener which display the popup menu. Going to try the snapshot now.

I am intending to solely change to FlatLaf and support for other LaF will be eliminated.

@derreisende77
Copy link
Author

2.4-SNAPSHOT fixes the problem :) I cannot reproduce the exception on linux. Going to test on the other OS as well.

@derreisende77
Copy link
Author

macOS and Windows still work as expected. I would consider the issue fixed. Do you want me to close it?

Thank you very much!

@DevCharly
Copy link
Collaborator

Great 😄

@mchmaj
Copy link

mchmaj commented Jun 3, 2022

The issue only appeared on Linux because different platforms have different popup triggers - in Linux the popup menu is triggered by the mouse pressed event - so the draggedColumn is still set to a column that is probably no longer valid - and in Windows (and MacOS, I suppose) by mouse released, so after the dragged column is changed to null.

@DevCharly
Copy link
Collaborator

@mchmaj thanks for the info. This solves the mysterry 😄 Didn't know that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants