Question

I was trying to updating JComboBox with setModel(). However, it throws a java.lang.IllegalStateException. Specifically, I used a JTextField to point to the Editable component of the JComboBox, and Update the JComboBox every time the JTextField recieves a new input.

Can anyone tell me the reason?

package ui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;

import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.JToolBar;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

public class TestFrame extends JFrame implements DocumentListener {

    private static final long serialVersionUID = 1L;

    // tool bar
    private JToolBar topToolBar;
    private JTextField wordSearchField;
    private JComboBox<String> wordSearchTips;

    // window size
    private static final int width = 700;
    private static final int height = 500;

    public TestFrame() {
    }

    public static void main(String[] argv) {
        new TestFrame().CreateUI();
    }

    public void CreateUI() {
        setPreferredSize(new Dimension(width, height));
        setResizable(false);
        setLayout(new BorderLayout());

        // bottom
        JPanel bottom = new JPanel();
        bottom.setPreferredSize(new Dimension(width, 480));
        bottom.setLayout(new BorderLayout());

        // top
        topToolBar = new JToolBar();
        topToolBar.setBackground(Color.WHITE);
        topToolBar.setPreferredSize(new Dimension(width, 30));

        wordSearchTips = new JComboBox<String>();
        wordSearchTips.setEditable(true);
        wordSearchTips.setSelectedIndex(-1);
        wordSearchField = (JTextField) wordSearchTips.getEditor()
                .getEditorComponent();
        wordSearchField.getDocument().addDocumentListener(this);

        topToolBar.add(wordSearchTips);

        add(topToolBar, BorderLayout.NORTH);
        add(bottom, BorderLayout.SOUTH);

        pack();
        setVisible(true);

    }

    @Override
    public void changedUpdate(DocumentEvent e) {

    }

    @Override
    public void insertUpdate(DocumentEvent e) {

        String keyword = wordSearchField.getText().trim();
        DefaultComboBoxModel<String> m = new DefaultComboBoxModel<String>();
        ;
        for (int i = 0; i < 10; i++) {
            m.addElement(i + "");
        }
        wordSearchTips.setModel(m);
        wordSearchTips.setSelectedIndex(-1);
        ((JTextField) wordSearchTips.getEditor().getEditorComponent())
                .setText(keyword);
        wordSearchTips.showPopup();
    }

    @Override
    public void removeUpdate(DocumentEvent e) {
    }
}

Here is the exceptions:

Exception in thread "AWT-EventQueue-0" java.lang.IllegalStateException: Attempt to mutate in notification
    at javax.swing.text.AbstractDocument.writeLock(Unknown Source)
    at javax.swing.text.AbstractDocument.replace(Unknown Source)
    at javax.swing.text.JTextComponent.setText(Unknown Source)
    at javax.swing.plaf.metal.MetalComboBoxEditor$1.setText(Unknown Source)
    at javax.swing.plaf.basic.BasicComboBoxEditor.setItem(Unknown Source)
    at javax.swing.JComboBox.configureEditor(Unknown Source)
    at javax.swing.plaf.basic.BasicComboBoxUI$Handler.propertyChange(Unknown Source)
    at javax.swing.plaf.basic.BasicComboBoxUI$PropertyChangeHandler.propertyChange(Unknown Source)
    at javax.swing.plaf.metal.MetalComboBoxUI$MetalPropertyChangeListener.propertyChange(Unknown Source)
    at java.beans.PropertyChangeSupport.fire(Unknown Source)
    at java.beans.PropertyChangeSupport.firePropertyChange(Unknown Source)
    at java.beans.PropertyChangeSupport.firePropertyChange(Unknown Source)
    at java.awt.Component.firePropertyChange(Unknown Source)
    at javax.swing.JComboBox.setModel(Unknown Source)
    at ui.TestFrame.insertUpdate(TestFrame.java:94)
    at javax.swing.text.AbstractDocument.fireInsertUpdate(Unknown Source)
    at javax.swing.text.AbstractDocument.handleInsertString(Unknown Source)
    at javax.swing.text.AbstractDocument.insertString(Unknown Source)
    at javax.swing.text.PlainDocument.insertString(Unknown Source)
    at javax.swing.text.AbstractDocument.replace(Unknown Source)
    at javax.swing.text.JTextComponent.replaceSelection(Unknown Source)
    at javax.swing.text.DefaultEditorKit$DefaultKeyTypedAction.actionPerformed(Unknown Source)
    at javax.swing.SwingUtilities.notifyAction(Unknown Source)
    at javax.swing.JComponent.processKeyBinding(Unknown Source)
    at javax.swing.JComponent.processKeyBindings(Unknown Source)
    at javax.swing.JComponent.processKeyEvent(Unknown Source)
    at java.awt.Component.processEvent(Unknown Source)
    at java.awt.Container.processEvent(Unknown Source)
    at java.awt.Component.dispatchEventImpl(Unknown Source)
    at java.awt.Container.dispatchEventImpl(Unknown Source)
    at java.awt.Component.dispatchEvent(Unknown Source)
    at java.awt.KeyboardFocusManager.redispatchEvent(Unknown Source)
    at java.awt.DefaultKeyboardFocusManager.dispatchKeyEvent(Unknown Source)
    at java.awt.DefaultKeyboardFocusManager.preDispatchKeyEvent(Unknown Source)
    at java.awt.DefaultKeyboardFocusManager.typeAheadAssertions(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$200(Unknown Source)
    at java.awt.EventQueue$3.run(Unknown Source)
    at java.awt.EventQueue$3.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
    at java.awt.EventQueue$4.run(Unknown Source)
    at java.awt.EventQueue$4.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$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)
Was it helpful?

Solution

Wrap your code from the insertUpdate(DocumentEvent e) in SwingUtilities.invokeLater()

OTHER TIPS

Doing this can produce some very weird results. The API is designed to protect against threaded updates and potential modifications that might cause infinite loops

In this case, the text field is modified, the Document is updated, the DocumentListener is notified and you try to change the field again, which would start the cycle again, except the Document has a guard to prevent you from doing just this very thing, hence the Exception.

What you need to do is two things.

  1. You need to update the text field AFTER the DocumentListener has been notified. As has already been stated, this can be achieved by using SwingUtilities.invokeLater
  2. Provide some kind of flag so you don't try and modify your field on events cause by you modifying the field due to DocumentListener events...confused yet?

Basically, this example uses SwingUtilities.invokeLater to update the field AFTER the DocumentListener has completed it's event notifications and a simple boolean flag to provide a means to "ignore" updates that we have triggered...

It's messy, but it will get the job done...mostly...

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.JToolBar;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

public class TestFrame extends JFrame implements DocumentListener {

    private static final long serialVersionUID = 1L;

    // tool bar
    private JToolBar topToolBar;
    private JTextField wordSearchField;
    private JComboBox<String> wordSearchTips;

    // window size
    private static final int width = 700;
    private static final int height = 500;

    public TestFrame() {
    }

    public static void main(String[] argv) {
        new TestFrame().CreateUI();
    }

    public void CreateUI() {
        setPreferredSize(new Dimension(width, height));
        setResizable(false);
        setLayout(new BorderLayout());

        // bottom
        JPanel bottom = new JPanel();
        bottom.setPreferredSize(new Dimension(width, 480));
        bottom.setLayout(new BorderLayout());

        // top
        topToolBar = new JToolBar();
        topToolBar.setBackground(Color.WHITE);
        topToolBar.setPreferredSize(new Dimension(width, 30));

        wordSearchTips = new JComboBox<String>();
        wordSearchTips.setEditable(true);
        wordSearchTips.setSelectedIndex(-1);
        wordSearchField = (JTextField) wordSearchTips.getEditor()
                .getEditorComponent();
        wordSearchField.getDocument().addDocumentListener(this);

        topToolBar.add(wordSearchTips);

        add(topToolBar, BorderLayout.NORTH);
        add(bottom, BorderLayout.SOUTH);

        pack();
        setVisible(true);

    }

    @Override
    public void changedUpdate(DocumentEvent e) {
    }

    boolean beenModified = false;

    @Override
    public void insertUpdate(DocumentEvent e) {

        if (!beenModified) {
            beenModified = true;

            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {

                    String keyword = wordSearchField.getText().trim();
                    DefaultComboBoxModel<String> m = new DefaultComboBoxModel<String>();
                    for (int i = 0; i < 10; i++) {
                        m.addElement(i + "");
                    }
                    wordSearchTips.setModel(m);
                    wordSearchTips.setSelectedIndex(-1);
                    ((JTextField) wordSearchTips.getEditor().getEditorComponent())
                            .setText(keyword);
                    wordSearchTips.showPopup();

                    beenModified = false;
                }
            });

        }
    }

    @Override
    public void removeUpdate(DocumentEvent e) {
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top