vendredi 9 août 2013

java.lang.IndexOutOfBoundsException: "Invalid index" lors de l'appel à la méthode JFileChooser.setCurrentDirectory( dir )

Une exception assez inattendue peut survenir lors de l'appel à la méthode setCurrentDirectory() sur une objet de type JFileChooser.

Exception in thread "xxx" java.lang.IndexOutOfBoundsException: Invalid index
 at javax.swing.DefaultRowSorter.convertRowIndexToModel(DefaultRowSorter.java:514)
 at sun.swing.FilePane$SortableListModel.getElementAt(FilePane.java:658)
 at javax.swing.plaf.basic.BasicListUI.updateLayoutState(BasicListUI.java:1360)
 at javax.swing.plaf.basic.BasicListUI.maybeUpdateLayoutState(BasicListUI.java:1311)
 at javax.swing.plaf.basic.BasicListUI.getCellBounds(BasicListUI.java:952)
 at javax.swing.JList.getCellBounds(JList.java:1633)
 at javax.swing.JList.ensureIndexIsVisible(JList.java:1149)
 at sun.swing.FilePane.ensureIndexIsVisible(FilePane.java:1694)
 at sun.swing.FilePane.doDirectoryChanged(FilePane.java:1617)
 at sun.swing.FilePane.propertyChange(FilePane.java:1667)
 at java.beans.PropertyChangeSupport.fire(PropertyChangeSupport.java:335)
 at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:327)
 at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:263)
 at java.awt.Component.firePropertyChange(Component.java:8382)
 at javax.swing.JFileChooser.setCurrentDirectory(JFileChooser.java:581)
 at ...

A priori cela ressemble à un bug Swing, or il n'en est rien. Un article présent dans la base de bug de Sun, explique qu'il ne s'agit pas d'un bug, mais d'un problème de Thread.

Il est a noter que dans mon cas, qu'aucune erreur n'était visible tant que le code ne faisait pas appel à la méthode setCurrentDirectory().

Pour ceux qui sont pressés, je vous donne directement la recette de cuisine :
Il suffit de transformer le code qui initialise votre objet JFileChooser comme suit

Code initial :
JFileChooser jfc = ...
jfc.setFileSelectionMode( ... );
jfc.setCurrentDirectory( ... );
if( jfc.showOpenDialog( parent ) == JFileChooser.APPROVE_OPTION ){
 ...
 }

Code corrigé :
SwingUtilities.invokeLater( new Runnable() { @Override public void run() { JFileChooser jfc = ... jfc.setFileSelectionMode( ... ); jfc.setCurrentDirectory( ... ); if( jfc.showOpenDialog( parent ) == JFileChooser.APPROVE_OPTION ){ ... } } });

Comme l'explique, fort mal, l'article de Sun, l’initialisation doit se faire depuis l'"event dispatch thread", en français, depuis le thread de gestion des événements Swing.

Dans les programmes Swing, le thread d'initialisations n'ont pas pas grand chose à faire. Sa tâche principal étant de créer un objet Runnable qui initialise l'interface graphique. Cette tâche est mise dans la pile des tâche awt/swing, c'est depuis cet environnement que doit être exécuté les traitements lié à l'interface graphique.

Une fois l'interface graphique est créé, le programme est principalement contrôlé par les événements GUI, chacun qui provoque l'exécution d'une tâche courte sur l'"event dispatch thread". Le code d'application peut planifier des tâches de suppléments sur l'"event dispatch thread" a condition qu'ils se terminent rapidement, afin de ne pas interférer avec le traitement des événements ou un thread de travail (pour les tâches longue à traiter).

Aucun commentaire:

Enregistrer un commentaire