PUBLIC OBJECT

Swinger Hack: Blow your stack

Here's a common Swing problem. You're a _XYZ_Listener and you've just received a fresh new _XYZ_Event. So you make some changes to XYZ, coloring it blue, doubling its value and removing its tartar sauce attributes. Unfortunately this all fails with an unpleasant error:

_XYZ_ cannot be modified by _XYZ_'s own Listener

Here's when this happens:

  • _XYZ_Listener is a _Document_Listener and the thing you want to remove tartar sauce from is the _Document_. The Document slaps you when you try this.
  • _XYZ_Listener is a _ListSelectionModel_Listener and the thing you want to change is the _ListSelectionModel_. The Listener might let this happen, but you're breaking the rules and that's a bad thing.

    The recommended solution
    All over the Internet forums, people hurt you by asking you to work around this problem in painful ways:

  • Subclass PlainDocument or DefaultListSelectionModel
  • Write your own implementation of Document or ListSelectionModel

    my 1337 hack solution
    Instead of fighting the locks by changing your code, you can get outside of them by changing when your code executes: Post a Runnable the the event queue to solve the problem later! So although this code doesn't work:
    <pre> public void changedUpdate(DocumentEvent e) { String uppercase = myTextField.getText().toUpperCase(); myTextField.setText(uppercase); }</pre>
    This code changes the time that setText() is executed, to happen after the listeners have all been notified:
    <pre> public void changedUpdate(DocumentEvent e) { final String uppercase = myTextField.getText().toUpperCase(); // avoid infinite loops if(uppercase.equals(myTextField.getText())) return; // schedule update SwingUtilities.invokeLater(new Runnable() { public void run() { myTextField.setText(uppercase); } }); }</pre>

    Note that although this hack will help you get your work done, it has some weaknesses when compared to the recommended solution:

  • Your _XYZ_ will be in an intermediate state for a microsecond between the original change and your update that follows.
  • You need to avoid infinite loops by not calling invokeAndWait() in response to events that you initiated.