19 abril 2006

Desuscribirse sobre la marcha

Hace poco me he encontrado con un pequeño problema, que es el siguiente.

Tengo una clase que hereda de JTable. Esta clase en cierta ocasión debe mostrar un JDialog. Para que toda vaya bien, necesito que el padre de ese JDialog sea la ventana en la que está mi JTable. Como estoy dentro de la clase JTable, no tengo garantía ninguna de que mi clase JTable esté todavía dentro de ninguna ventana, así que no hay un sitio claro donde crear este JDialog hijo.

La solución que se me había ocurrido consiste en hacer un HierarchyListener y suscribirlo a mi clase JTable. De esta forma, cuando metan el JTable dentro de algún componente, compruebo si ya tengo ventana (con SwingUtilities.getWindowAncestor(miJTable)). Si ya tengo ventana, creo el JDialog pasándole el padre.

Como el JDialog sólo quiero crearlo una vez, lo que hago inmediatamente después de crearlo es desuscribir el HierarchyListener. Ahí me saltó el problema.

En las primeras pruebas que hice, todo correcto. Sin embargo, al meter mi clase JTable en una aplicación con interfaces de usuario más complejas, me saltaba una excepción en el momento de meter el JTable dentro de un JPanel. Además, la excepción no era en mi código, sino en las clases internas de java. La primera línea de código mia implicada en la excepción era simplemente meter el JTable en un JPanel, con todo correcto.

El problema es que java está avisando a los HierarchyListener de mi JTable. Primero mira cuantos hay, se lo guarda en una variable y luego se mete en un bucle para ir avisándolos. Como en ese mismo aviso yo me desuscribía, su lista de HierarchyListener decrecía en un elemento. El bucle, al llegar al último índice, daba una excepción al tratar de recuperar el último elemento de la lista que ahora es de un elemento menos (el número de elementos de la lista estaba previamente guardado en una variable).

La solución que he puesto es desuscribirme con un SwingUtilities.invokeLater(). De esta forma la desuscripción se realiza más adelante, cuando se pueda, y el bucle de aviso a los suscriptores termina de forma normal.

Resumiendo, un listener de cualquier cosa no puede desuscribirse alegremente de un sitio en el mismo método de aviso. En mi caso, no puedo desuscribir mi HierarchyListener en el método hierarchyChanged(). Según como esté implmentado el recorrer la lista de suscriptores/listeners para avisarles, esto puede provocar una excepción.

No hay comentarios: