Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>The <code>java.lang.IllegalStateException: Attempt to mutate in notification</code> is thrown because in these lines in <code>FileContentSubject</code>:</p> <pre><code>@Override public void insertUpdate(DocumentEvent arg0) { doc = (Document) arg0.getDocument(); try { this.textArea.setText(doc.getText(0, doc.getLength()-1)); } catch (BadLocationException e) { // TODO Auto-generated catch block e.printStackTrace(); } notifyAllObservers(); } @Override public void removeUpdate(DocumentEvent arg0) { doc = (Document) arg0.getDocument(); try { this.textArea.setText(doc.getText(0, doc.getLength()-1)); } catch (BadLocationException e) { // TODO Auto-generated catch block e.printStackTrace(); } notifyAllObservers(); } </code></pre> <p>And this line at <code>FileContentSubject</code>'s constructor:</p> <pre><code>textArea.getDocument().addDocumentListener(this); </code></pre> <p><strong>show that you are trying to change the value of the <code>textArea</code> from inside the <code>DocumentListener</code> methods.</strong></p> <p>As pointed out <a href="https://stackoverflow.com/questions/6088069/java-cant-change-the-value-of-a-jtextfield-from-inside-of-documentlistener-met">here</a>, <strong>you should use a <code>DocumentFilter</code> for that purpose</strong>.</p> <p>Here's a simple working example of <code>DocumentFilter</code>'s use (it replaces all typed chars with their uppercase version):</p> <pre><code>//UpcaseFilter.java //A simple DocumentFilter that maps lowercase letters to uppercase. import javax.swing.*; import javax.swing.text.*; public class UpcaseFilter extends DocumentFilter { public void insertString(DocumentFilter.FilterBypass fb, int offset, String text, AttributeSet attr) throws BadLocationException { fb.insertString(offset, text.toUpperCase(), attr); } // no need to override remove(): inherited version allows all removals public void replace(DocumentFilter.FilterBypass fb, int offset, int length, String text, AttributeSet attr) throws BadLocationException { fb.replace(offset, length, text.toUpperCase(), attr); } public static void main(String[] args) { DocumentFilter dfilter = new UpcaseFilter(); JTextArea jta = new JTextArea(); JTextField jtf = new JTextField(); ((AbstractDocument) jta.getDocument()).setDocumentFilter(dfilter); ((AbstractDocument) jtf.getDocument()).setDocumentFilter(dfilter); JFrame frame = new JFrame("UpcaseFilter"); frame.getContentPane().add(jta, java.awt.BorderLayout.CENTER); frame.getContentPane().add(jtf, java.awt.BorderLayout.SOUTH); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(200, 120); frame.setVisible(true); } } </code></pre> <p>(The above example was taken from the <a href="http://shop.oreilly.com/product/9780596004088.do" rel="nofollow noreferrer">Java Swing, 2nd Edition</a> book, <a href="http://examples.oreilly.com/jswing2/code/ch22/UpcaseFilter.java" rel="nofollow noreferrer">chapter 22</a>.)</p> <h2>That's the reason for the exception thrown, but you'll have to do more to correct you code:</h2> <p>In short, the problem is that this happens in your code:</p> <ul> <li>When an <code>Editor</code> instance's (let's call it <code>edt</code>) <code>textArea</code> changes...</li> <li>...the <code>FileContentSubject</code> (as it is a <code>DocumentListener</code>) notices the event and then notifies all its registered observers (<code>edt</code> included!) to call their <code>update()</code> method...</li> <li>...the <code>update()</code> method of <code>edt</code> changes its <code>textArea</code> and... voilá! You are trying to change the <code>textArea</code> who started the event (before the event ends)!</li> </ul> <p>All you have to do then is find a way of not notifying who started the event. The code below uses <code>Document.putProperty()</code> and <code>Document.getProperty()</code> for that: it detaches the event's source editor (<code>reference.detach(e);</code>), notifies everyone of the change, and reattaches it (<code>reference.attach(e);</code>).</p> <p>(Also, I replaced <code>FileContentSubject</code>'s <code>JTextArea</code> with a <code>String</code>, as just that <code>String</code> will suffice.)</p> <p>So, heres the changed <code>FileContentSubject</code> code:</p> <pre><code>import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.text.BadLocationException; import javax.swing.text.Document; public class FileContentSubject implements Subject, DocumentListener { // private JTextArea textArea; // removed as a String field will suffice // private Document doc; // should not be a field! private String state; public String getState() { return this.state; } private SubjectImpl reference; @Override public void attach(Observer o) { reference.attach(o); } @Override public void detach(Observer o) { reference.detach(o); } @Override public void notifyAllObservers() { reference.notifyAllObservers(); } public FileContentSubject() { reference = new SubjectImpl(); // textArea = new JTextArea(); // textArea.setTabSize(5); // textArea.setLineWrap(true); // textArea.setWrapStyleWord(true); // textArea.getDocument().addDocumentListener(this); } @Override public void changedUpdate(DocumentEvent arg0) { } @Override public void insertUpdate(DocumentEvent arg0) { Document doc = (Document) arg0.getDocument(); try { // this.textArea.setText(doc.getText(0, doc.getLength()-1)); this.state = doc.getText(0, doc.getLength()); } catch (BadLocationException e) { // TODO Auto-generated catch block e.printStackTrace(); } Editor e = (Editor) doc.getProperty("ownerEditor"); reference.detach(e); // so it will not be notified of its own change notifyAllObservers(); // tell everybody else to catch up with the changes reference.attach(e); // reattaches the editor } @Override public void removeUpdate(DocumentEvent arg0) { Document doc = (Document) arg0.getDocument(); try { // this.textArea.setText(doc.getText(0, doc.getLength()-1)); this.state = doc.getText(0, doc.getLength()); } catch (BadLocationException e) { // TODO Auto-generated catch block e.printStackTrace(); } Editor e = (Editor) doc.getProperty("ownerEditor"); reference.detach(e); // so it will not be notified of its own change notifyAllObservers(); // tell everybody else to catch up with the changes reference.attach(e); // reattaches the editor } // public String getJTextArea() { // return textArea.getText(); // } } </code></pre> <p>The changed <code>Editor</code>'s constructor:</p> <pre><code>public Editor(FileContentSubject filecontentsubject) { super("Editor"); initComponents(); this.reference = filecontentsubject; textArea.getDocument().addDocumentListener(reference); textArea.getDocument().putProperty("ownerEditor", this); // &lt;---- ADDED LINE } </code></pre> <p>and <code>update()</code>:</p> <pre><code>@Override public void update() { //textArea.setText(reference.getJTextArea()); //textArea.setText(reference.temp); textArea.setText(reference.getState()); // ADDED } </code></pre> <p>That's it!</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload