I’m working on a Sudoku puzzle generator and am running into some intermittent swing exceptions after/during a call to the RemoveAll() method of a JPanel. When I run in eclipse’s debug mode, the exceptions do not appear. Here is the code for the class in question:
import java.awt.FlowLayout;
import java.awt.GridLayout;
import javax.swing.JLabel;
import javax.swing.JPanel;
/**
* Represents a cell in the GUI Grid display
* @author alex
*
*/
public class CellGUI extends JPanel {
public CellGUI()
{
super();
this.setLayout(new GridLayout(3,3));
for(int i = 1;i <=9;i++)
{
add(new JLabel("" + i));
}
setVisible(true);
}
public void clear()
{
this.removeAll();
this.validate();
this.setLayout(new GridLayout(3,3));
for(int i = 1;i <= 9; i++)
{
add(new JLabel("" + i));
}
}
public void setValue(int newVal)
{
if (newVal == 0)
{
clear();
}
else
{
this.removeAll(); // this line appears to be the problem
//this.updateUI();
//this.setLayout(new FlowLayout());
//add(new JLabel("" + newVal));
}
}
}
It usually spits out this exception:
Exception in thread "AWT-EventQueue-0" java.lang.ClassCastException
at javax.swing.LayoutComparator.compare(Unknown Source)
at java.util.Arrays.mergeSort(Unknown Source)
at java.util.Arrays.mergeSort(Unknown Source)
at java.util.Arrays.mergeSort(Unknown Source)
at java.util.Arrays.mergeSort(Unknown Source)
at java.util.Arrays.mergeSort(Unknown Source)
at java.util.Arrays.mergeSort(Unknown Source)
at java.util.Arrays.mergeSort(Unknown Source)
at java.util.Arrays.sort(Unknown Source)
at java.util.Collections.sort(Unknown Source)
at javax.swing.SortingFocusTraversalPolicy.enumerateAndSortCycle(Unknown Source)
at javax.swing.SortingFocusTraversalPolicy.getFirstComponent(Unknown Source)
at javax.swing.LayoutFocusTraversalPolicy.getFirstComponent(Unknown Source)
at javax.swing.SortingFocusTraversalPolicy.getDefaultComponent(Unknown Source)
at java.awt.FocusTraversalPolicy.getInitialComponent(Unknown Source)
at java.awt.DefaultKeyboardFocusManager.dispatchEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Window.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$000(Unknown Source)
at java.awt.EventQueue$1.run(Unknown Source)
at java.awt.EventQueue$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.AccessControlContext$1.doIntersectionPrivilege(Unknown Source)
at java.security.AccessControlContext$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue$2.run(Unknown Source)
at java.awt.EventQueue$2.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.AccessControlContext$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.SequencedEvent.dispatch(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$000(Unknown Source)
at java.awt.EventQueue$1.run(Unknown Source)
at java.awt.EventQueue$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.AccessControlContext$1.doIntersectionPrivilege(Unknown Source)
at java.security.AccessControlContext$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue$2.run(Unknown Source)
at java.awt.EventQueue$2.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.AccessControlContext$1.doIntersectionPrivilege(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)
I figure in my naivety I’m probably going about doing this the wrong way. Any ideas?
Edit: By request, I’m posting additional code.
This class calls the setValue method of the CellGUI:
public class GridGUI extends JPanel {
ArrayList<CellGUI> cells = new ArrayList<CellGUI>();
public GridGUI()
{
this.setLayout(new GridLayout(9,9));
for(int i = 0; i < 81;i++)
{
CellGUI cell = new CellGUI();
cells.add(cell);
add(cell);
}
}
public void updateGrid(Grid g)
{
for(int i = 0;i<81;i++)
{
cells.get(i).setValue(g.getValue(i));
}
}
}
The GridGUI class is created and managed by a class that extends JFrame:
public class GUI extends JFrame {
...
public GUI()
{
this.setLayout(new BorderLayout());
add(gridGUI,BorderLayout.CENTER);
JPanel panel = new JPanel();
panel.setLayout(new FlowLayout());
panel.add(isValidLabel);
panel.add(isSolvableLabel);
panel.setVisible(true);
add(panel, BorderLayout.SOUTH);
this.setSize(600, 600);
setVisible(true);
}
public void loadGrid(String path)
{
grid = new Grid(path);
grid.print();
gridGUI.updateGrid(grid);
updateLabels();
}
...
}
Which is called by my main class:
public static void main(String[] args)
{
GUI gui = new GUI();
gui.loadGrid(args[0]);
}
So, looking at the stack-trace shows that the error occurs when the FocusManager is trying to get the first component. For this, your FocusTraversalPolicy tries to order the components in “layout order”, which means roughly in columns and rows. For a GridLayout, this should be totally trivial. Let’s look at the code of
LayoutComparator. It throws a ClassCastException in some places:a is either the containing Window here, or
nullif no such Window exists.So, looks like your components are not yet totally registered in the Component tree. As the others said, this can occur if you do some changes in the GUI when not being in the AWT event dispatch thread.
To avoid this, wrap all changes to the GUI (including creating the components, like your constructor call above) in a call of
EventQueue.invokeLater(...). (SometimesinvokeAndWaitis more useful, and you also can use the same-named methods in SwingUtilities instead.) In your case, the change is quite simple: