user2102972
user2102972

Reputation: 261

Getting 'Attempt to mutate notification' exception

My goal is to implement blue coloring of keywords written by user into JTextPane. This is how my code look like:

private class DocumentHandler implements DocumentListener {

        @Override
        public void changedUpdate(DocumentEvent ev) {
        }

        @Override
        public void insertUpdate(DocumentEvent ev) {
            highlight();
        }

        @Override
        public void removeUpdate(DocumentEvent ev) {
            highlight();
        }

        private void highlight() {
            String code = codePane.getText();
            SimpleAttributeSet defSet = new SimpleAttributeSet();
            StyleConstants.setForeground(defSet, Color.BLACK);
            doc.setCharacterAttributes(0, code.length(), defSet, true);
            SimpleAttributeSet set = new SimpleAttributeSet();
            StyleConstants.setForeground(set, Color.BLUE);
            for (String keyword : keywords) {
                Pattern pattern = Pattern.compile(keyword + "(\\[\\])*");
                Matcher matcher = pattern.matcher(code);
                while (matcher.find()) {

                    //Just for test
                    System.out.print("Start index: " + matcher.start());
                    System.out.print(" End index: " + matcher.end());
                    System.out.println(" Found: " + matcher.group());

                    doc.setCharacterAttributes(matcher.start(), keyword.length(), set, true);
                }
            }
        }
    }

After typing anything into pane I get:

Exception in thread "AWT-EventQueue-0" java.lang.IllegalStateException: Attempt to mutate in notification
    at javax.swing.text.AbstractDocument.writeLock(AbstractDocument.java:1338)
    at javax.swing.text.DefaultStyledDocument.setCharacterAttributes(DefaultStyledDocument.java:500)
    at jnotepad.MainPanel$DocumentHandler.highlight(MainPanel.java:121)
    at jnotepad.MainPanel$DocumentHandler.insertUpdate(MainPanel.java:108)
    at javax.swing.text.AbstractDocument.fireInsertUpdate(AbstractDocument.java:202)
    at javax.swing.text.AbstractDocument.handleInsertString(AbstractDocument.java:749)

How to solve my problem? Maybe I should use something other than DocumentListener?

Upvotes: 18

Views: 20528

Answers (2)

user2845064
user2845064

Reputation: 71

I had the same problem, I solved it by using this:

expiration_timeTF.getDocument().addDocumentListener(
            new DocumentListener() {
                @Override
                public void removeUpdate(DocumentEvent e) {
                    System.out.println("remove");
                }

                private void assistDateText() {
                    Runnable doAssist = new Runnable() {
                        @Override
                        public void run() {
                            // when input "2013",will add to "2013-";when
                            // input "2013-10",will add to "2013-10-"
                            String input = expiration_timeTF.getText();
                            if (input.matches("^[0-9]{4}")) {
                                expiration_timeTF.setText(input + "-");
                            } else if (input.matches("^[0-9]{4}-[0-9]{2}")) {
                                expiration_timeTF.setText(input + "-");
                            }
                        }
                    };
                    SwingUtilities.invokeLater(doAssist);
                }

                @Override
                public void insertUpdate(DocumentEvent e) {
                    // System.out.println("insert");
                    assistDateText();
                }

                @Override
                public void changedUpdate(DocumentEvent e) {
                    // System.out.println("change");
                }
            });

Upvotes: 4

moeTi
moeTi

Reputation: 3904

You need to invoke changes to the document from the Event Dispatcher Thread.

Try this:

private void highlight() {

    Runnable doHighlight = new Runnable() {
        @Override
        public void run() {
            // your highlight code
        }
    };       
    SwingUtilities.invokeLater(doHighlight);
}

Upvotes: 31

Related Questions