Hot questions for Using JTextField in documentlistener

Top Java Programmings / JTextField / documentlistener

Question:

It is mentioned in the comments of this question and in answers of this question that documentListener or documentFilter should be used rather than keyListener. Why is this?

Within a simple Hangman game I had been writing the other day I had done something like the code below:

public class Hangman extends JFrame implements KeyListener, ActionListener{
    private JTextField guess;
    private JButton b1;
    private void addComponentsToPane(){
        b1 = new JButton("New Game");
        b1.addActionListener(this);
        guess = new JTextField(2);
        guess.addKeyListener(this);

Later in the code I used keyTyped(KeyEvent ke) to get text input from the JTextField and actionPerformed(ActionEvent ae) for the JButton.

My question is, should documentListener/documentFilter be used rather than keyListener, and why or why not?


Answer:

  • KeyListener -- you should almost never use this with a text component as it is a low-level construct and can mess up the functioning of the text component.
  • DocumentListener -- use this if you're not going to filter the input -- if you're going to accept all input presented but just want to keep track of input.
  • DocumentFilter -- use if you are going to filter the input prior to display.

Myself, I'd use a DocumentFilter since this way I can ignore non-letter input. And I also second all that Kevin mentioned. 1+ to his answer.

Question:

I am making a ui in which there are two jtextfields. I want is when user inputs in the first jtextfield it should mirror in the second jtextfield with an increment of 1 and if the user inputs in the second JtextField it should mirror in the first jtextfield with a decrement of 1.But i am getting errors like IllegalStateException:Attempt to mutate in notification.

Sample Code:

public class Sh extends JFrame 
{
 public Sh() 
{
super("SH");
JPanel panel = new JPanel();
getContentPane().add(panel, BorderLayout.NORTH);

final JTextField field = new JTextField(10);
panel.add(field); 

final JTextField field1 = new JTextField(10);
panel.add(field1);

  DocumentListener docListener = new DocumentListener(){

        @Override
        public void changedUpdate(DocumentEvent e) {
            updateLabel(e);
        }

        @Override
        public void insertUpdate(DocumentEvent e) {
            updateLabel(e);
        }

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

        private void updateLabel(DocumentEvent e) {
     String text = field.getText();
                    String text1 = field1.getText();

                    if (!text.isEmpty()) {
                        int p = Integer.parseInt(text);
                        int i = (p + 1);
                        String s = String.valueOf(i);
                        field1.setText(s);
                    } else if (!text1.isEmpty()) {
                        int p = Integer.parseInt(text1);
                        int i = (p - 1);
                        String s = String.valueOf(i);
                        field.setText(s);
                    }

                }
    };
  field.getDocument().addDocumentListener(docListener);
  field1.getDocument().addDocumentListener(docListener); 

       DocumentFilter docFilter = new DocumentFilter(){
        @Override
        public void insertString(FilterBypass fb, int off, String str, AttributeSet attr)
                throws BadLocationException {
            fb.insertString(off, str.replaceAll("\\D++", ""), attr);  // remove non-digits
        }

        @Override
        public void replace(FilterBypass fb, int off, int len, String str, AttributeSet attr)
                throws BadLocationException {
            fb.replace(off, len, str.replaceAll("\\D++", ""), attr);  // remove non-digits
        }
    };   
 ((AbstractDocument) field.getDocument()).setDocumentFilter(docFilter); 
 ((AbstractDocument) field1.getDocument()).setDocumentFilter(docFilter); 

  }
   public static void main(String[] args)
   {
  Sh s = new Sh();
  s.setDefaultCloseOperation(EXIT_ON_CLOSE);
  s.pack();
  s.setVisible(true);
   }
 }

Answer:

As proposed by the MadProgrammer here is the solution:

import java.awt.BorderLayout;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.AbstractDocument;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.DocumentFilter;

/**
 * <code>Sh</code>.
 *
 * @author SMedvynskyy
 */
public class Sh extends JFrame
{
    public Sh()
    {
        super("SH");
        final JPanel panel = new JPanel();
        getContentPane().add(panel, BorderLayout.NORTH);

        final JTextField field = new JTextField(10);
        panel.add(field);

        final JTextField field1 = new JTextField(10);
        panel.add(field1);

        final DocumentListener docListener = new DocumentListener(){

            private Document originator;

            @Override
            public void changedUpdate(DocumentEvent e) {
                updateLabel(e);
            }

            @Override
            public void insertUpdate(DocumentEvent e) {
                updateLabel(e);
            }

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

            private void updateLabel(DocumentEvent e) {
                if (null == originator) {
                    originator = e.getDocument();
                    String text = "";
                    try {
                        text = originator.getText(0, originator.getLength());
                    } catch (final Exception ex) {
                        ex.printStackTrace();
                    }

                    if (!text.isEmpty()) {
                        final int p = Integer.parseInt(text);
                        if (originator.equals(field.getDocument())) {
                            final int i = (p + 1);
                            final String s = String.valueOf(i);
                            field1.setText(s);
                        } else {
                            final int i = (p - 1);
                            final String s = String.valueOf(i);
                            field.setText(s);
                        }
                    } else {
                      field.setText(text);
                      field1.setText(text);
                    }

                    originator = null;
                }

            }
        };
        field.getDocument().addDocumentListener(docListener);
        field1.getDocument().addDocumentListener(docListener);

        final DocumentFilter docFilter = new DocumentFilter(){
            @Override
            public void insertString(FilterBypass fb, int off, String str, AttributeSet attr)
                    throws BadLocationException {
                fb.insertString(off, str.replaceAll("\\D++", ""), attr);  // remove non-digits
            }

            @Override
            public void replace(FilterBypass fb, int off, int len, String str, AttributeSet attr)
                    throws BadLocationException {
                fb.replace(off, len, str.replaceAll("\\D++", ""), attr);  // remove non-digits
            }
        };
        ((AbstractDocument) field.getDocument()).setDocumentFilter(docFilter);
        ((AbstractDocument) field1.getDocument()).setDocumentFilter(docFilter);

    }
    public static void main(String[] args)
    {
        final Sh s = new Sh();
        s.setDefaultCloseOperation(EXIT_ON_CLOSE);
        s.pack();
        s.setVisible(true);
    }
}

Method JTextField.setText provokes a DocumentEvent to be fired, so if you don't avoid it especially - you will get this problem (or an infinite recursion).

Question:

My english is very bad so I couldn't find anything on Google, I have a JFrame with a JPanel that contains a lot of JTextField. When I was creating the "save file" feature I had to put a KeyListener for every JTextField. Is there a more efficient way of doing it?

EDIT: is to test if any of the fields have been modified since last save


Answer:

Regarding,

EDIT: is to test if any of the fields have been modified since last save

Then use a DocumentListener. This way you could listen for changes that occur beyond key presses such as with cut and paste, and still respond the same.

You ask:

this looks cool, but is there any way of adding it to the JPanel or I need to add it to all the fields?

You would need to add the listener to each field's Document, but if you put the fields in a list or an array, it would be easy to do with a simple for loop. For instance run the following code. Whenever the save button is pressed, or when it is activated by pressing alt-s, then it becomes disabled. It is automatically enabled by any changes to the documents held by any JTextField.

import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;

import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

public class DocListenerEg extends JPanel {
   private static final int FIELD_COUNT = 10;
   private static final int COLUMNS = 8;
   private JButton saveButton = new JButton(new SaveAction("Save", KeyEvent.VK_S));
   private List<JTextField> fieldList = new ArrayList<>();

   public DocListenerEg() {
      MyDocListener docListener = new MyDocListener();
      add(saveButton);
      for (int i = 0; i < FIELD_COUNT; i++) {
         JTextField field = new JTextField(COLUMNS);
         add(field);
         fieldList.add(field);

         field.getDocument().addDocumentListener(docListener);
      }
   }

   public void documentChange() {
      saveButton.setEnabled(true);
   }

   private class MyDocListener implements DocumentListener {
      private boolean changed = false;

      @Override
      public void changedUpdate(DocumentEvent dEvt) {
         documentChange();
      }

      @Override
      public void insertUpdate(DocumentEvent dEvt) {
         documentChange();
      }

      @Override
      public void removeUpdate(DocumentEvent dEvt) {
         documentChange();
      }

   }

   private class SaveAction extends AbstractAction {

      public SaveAction(String name, int mnemonic) {
         super(name);
         putValue(MNEMONIC_KEY, mnemonic);
      }

      @Override
      public void actionPerformed(ActionEvent evt) {
         // TODO: save the data
         ((AbstractButton) evt.getSource()).setEnabled(false);
      }

   }

   private static void createAndShowGui() {
      DocListenerEg mainPanel = new DocListenerEg();

      JFrame frame = new JFrame("DocListenerEg");
      frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

Question:

I have sticked below listener inside JtextField when action performed event, so as to perform action against any change made in text box once user make any change. but the problem is the code not starting or working unless you press enter only then the code execute the code, i need to know what i have to add and where to enable below code once text Filed changed instantly .I can see some similar help referring to Oracle listener help but am unable to manage so i need direct simple way.

private void jTextField1ActionPerformed(java.awt.event.ActionEvent evt) {                                            
jTextField1.getDocument().addDocumentListener(new DocumentListener() {

public void changedUpdate(DocumentEvent e) {
JOptionPane.showMessageDialog(null, "Change case");
} 
public void insertUpdate(DocumentEvent de) {
    JOptionPane.showMessageDialog(null, "Update Case");
}

public void removeUpdate(DocumentEvent de) {
  JOptionPane.showMessageDialog(null, "Remove case");
 }
});
 // TODO add your handling code here:
}                                           

Answer:

ActionListener for a text field only listens for enter key being typed. So what your code essentialy does is: when enter key is pressed adds a new DocumentListener to the text field.

The DocumentListener is what you want so take that code (adding the document listener) out of the jTextField1ActionPerformed method and put it in the constructor of the class. Or have a private method, so as not to clutter the constructor.

Assuming you are using Netbeans GUI editor (looks like it from your method signature):

public class MyFrame exentds JFrame {
    public MyFrame() {
        initComponents();
        addDocumentListenerToField();
    }

    private void addDocumentListenerToField() {
        jTextField.getDocument().addDocumentListener(..);
    }
}

UPDATE: DEMO

import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

public class DocListeenerDemo extends JFrame {
    private JTextField field;   
    private JLabel label;

    public DocListeenerDemo() {
        initComponents();
        addDocumentListenerToField();
    }

    private void initComponents() {
        setLayout(new GridLayout(0, 1));
        field = new JTextField(20);
        label = new JLabel("", SwingConstants.CENTER);
        add(field);
        add(label);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        pack();
        setLocationByPlatform(true);
    }

    private void addDocumentListenerToField() {
        field.getDocument().addDocumentListener(new DocumentListener(){
            public void changedUpdate(DocumentEvent arg0) {doYourStuff();}
            public void insertUpdate(DocumentEvent arg0) {doYourStuff();}
            public void removeUpdate(DocumentEvent arg0) {doYourStuff();}
            public void doYourStuff() {
                label.setText(field.getText());
            }
        });
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable(){
            public void run() {
                DocListeenerDemo demo = new DocListeenerDemo();
                demo.setVisible(true);
            }
        });
    }
}

I have not figure out a way to add the DocumentListener through the GUI tool. Sucks.

Question:

for the following Code I get an IllegalStateException (Attempt to mutate in notification):

private class DocumentHandler implements DocumentListener {
    public void changedUpdate(DocumentEvent ev) {
        // unused
    }
    public void insertUpdate(DocumentEvent ev) {    
        if(textInput.getText().equals("...")) {
        JOptionPane.showMessageDialog(null, "...");
        textInput.setText("");
    }
}

Why can´t I change a TextField while a DocumentListener is active?

I tried to remove the DocumentListener while the TextField is set to " " but that didn´t help at all. I know that someone asked a very similar question before, but I don´t get that answer...

Thanks


Answer:

In general, you don't -- you don't change the state of the Document while listening to it when using a DocumentListener. The two possible solutions that I know of:

  • From within your Listener, put the code that makes the changes that you wish to make within a Runnable and queue the Runnable onto the Swing event thread by calling SwingUtilities.invokeLater(yourRunnable). This is a shameless kludge
  • Much better: Don't use a DocumentListener but rather a DocumentFilter since this type of listener was geared towards making changes to the Document before the text is visualized within the component.

Unrelated side issue: your code shows a worrisome degree of coupling in that you try to change the text in a specific text component from within your listener. DocumentListeners should be fully agnostic of the text component whose document that they listen to, and in fact may be added to more than one Document.


A DocumentFilter has 3 methods that need to be overridden and do what you expect them to do: what you would expect them to do:

  • insertString: insert a String into the document
  • remove: removes text from the document
  • replace: replaces text in the document

What is more, these methods do their actions before the text component renders the changes to the document.

So within my method overrides, I extract the current document's text, and use the parameters to create what the new text will look like, for example for the replace method I did:

@Override
public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs)
        throws BadLocationException {
    String currentText = fb.getDocument().getText(0, fb.getDocument().getLength());
    StringBuilder sb = new StringBuilder(currentText);

    String newText = sb.replace(offset, offset + length, text).toString();

I then do a boolean test on the newText to see if it is "good", and if so, call the super's method, here replace(...), passing in all the parameters. If not, if the newText fails the test, then I remove all the text from the document and show a JOptionPane.

So in this example, I use this as my test method:

private boolean isTextOk(String text) {
    return !BAD_TEXTS.contains(text);
}

which tests to see if the text is any of the disallowed Strings, here "...", " ", "oops", "OOPS", but it could be any Strings that you desire. Again, if the text passes the text, call the super's method, else remove the text:

if (isTextOk(newText)) {
    super.replace(fb, offset, length, text, attrs);
} else {
    badText(fb);
}

Where badText(fb) does:

private void badText(FilterBypass fb) throws BadLocationException {
    remove(fb, 0, fb.getDocument().getLength());
    JOptionPane.showMessageDialog(null, "Don't do this!", "Bad Text Entered",
            JOptionPane.WARNING_MESSAGE);
}

The whole example is:

import java.util.Arrays;
import java.util.List;
import javax.swing.*;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter;
import javax.swing.text.PlainDocument;

@SuppressWarnings("serial")
public class ClearThreeDots extends JPanel {
    private JTextField textField = new JTextField(40);

    public ClearThreeDots() {
        ((PlainDocument) textField.getDocument()).setDocumentFilter(new MyDocFilter());
        add(textField);
    }

    private static void createAndShowGui() {
        ClearThreeDots mainPanel = new ClearThreeDots();

        JFrame frame = new JFrame("Clear Three Dots");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(mainPanel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}
class MyDocFilter extends DocumentFilter {
    private static final List<String> BAD_TEXTS = Arrays.asList("...", "   ", "oops", "OOPS");

    @Override
    public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr)
            throws BadLocationException {
        String currentText = fb.getDocument().getText(0, fb.getDocument().getLength());
        StringBuilder sb = new StringBuilder(currentText);

        String newText = sb.insert(offset, string).toString();

        if (isTextOk(newText)) {
            super.insertString(fb, offset, string, attr);
        } else {
            badText(fb);
        }
    }

    @Override
    public void remove(FilterBypass fb, int offset, int length) throws BadLocationException {
        String currentText = fb.getDocument().getText(0, fb.getDocument().getLength());
        StringBuilder sb = new StringBuilder(currentText);

        String newText = sb.replace(offset, offset + length, "").toString();

        if (isTextOk(newText)) {
            super.remove(fb, offset, length);
        } else {
            badText(fb);
        }

    }

    @Override
    public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs)
            throws BadLocationException {
        String currentText = fb.getDocument().getText(0, fb.getDocument().getLength());
        StringBuilder sb = new StringBuilder(currentText);

        String newText = sb.replace(offset, offset + length, text).toString();

        if (isTextOk(newText)) {
            super.replace(fb, offset, length, text, attrs);
        } else {
            badText(fb);
        }

    }

    private boolean isTextOk(String text) {
        return !BAD_TEXTS.contains(text);
    }

    private void badText(FilterBypass fb) throws BadLocationException {
        remove(fb, 0, fb.getDocument().getLength());
        JOptionPane.showMessageDialog(null, "Don't do this!", "Bad Text Entered",
                JOptionPane.WARNING_MESSAGE);
    }

}

Question:

I am trying to read data from a text file to Jtextfield1 and display it in jtextfield2,the data reads from text file to jtextfield1 but does not displays anything in jtextfield2 until we input a value in jtextfield1.I want as soon as the data is read in jtextfield1 it should reflect in jtextfield2.

 import java.awt.BorderLayout;
 import javax.swing.JFrame;
 import javax.swing.JPanel;
 import javax.swing.JTextField;
 import java.io.*;
 import java.util.*;
 import javax.swing.event.DocumentEvent;
 import javax.swing.event.DocumentListener;
 import javax.swing.text.AbstractDocument;
 import javax.swing.text.AttributeSet;
 import javax.swing.text.BadLocationException;
 import javax.swing.text.Document;
 import javax.swing.text.DocumentFilter;

 public class Sh extends JFrame
 {
  public Sh()
 {
    super("SH");
    final JPanel panel = new JPanel();
    getContentPane().add(panel, BorderLayout.NORTH);

    final JTextField field = new JTextField(10);
    panel.add(field);

  try{
        InputStream ips=new FileInputStream("test.txt"); 
        InputStreamReader ipsr=new InputStreamReader(ips);
        BufferedReader br=new BufferedReader(ipsr);
        String line;
        while ((line=br.readLine())!=null){
            field.setText(line);
          }
        br.close(); 
    }       
    catch (Exception e){
        e.printStackTrace();
    }


    final JTextField field1 = new JTextField(10);
    panel.add(field1);

    final DocumentListener docListener = new DocumentListener(){

        private Document originator;

        @Override
        public void changedUpdate(DocumentEvent e) {
            updateLabel(e);
        }

        @Override
        public void insertUpdate(DocumentEvent e) {
            updateLabel(e);
        }

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

        private void updateLabel(DocumentEvent e) {
            if (null == originator) {
                originator = e.getDocument();
                String text = "";
                try {
                    text = originator.getText(0, originator.getLength());
                } catch (final Exception ex) {
                    ex.printStackTrace();
                }

                if (!text.isEmpty()) {
                    final int p = Integer.parseInt(text);
                    if (originator.equals(field.getDocument())) {
                        final int i = (p + 1);
                        final String s = String.valueOf(i);
                        field1.setText(s);
                    } else {
                        final int i = (p - 1);
                        final String s = String.valueOf(i);
                        field.setText(s);
                    }
                } else {
                  field.setText(text);
                  field1.setText(text);
                }

                originator = null;
            }

        }
    };
    field.getDocument().addDocumentListener(docListener);
    field1.getDocument().addDocumentListener(docListener);

    final DocumentFilter docFilter = new DocumentFilter(){
        @Override
        public void insertString(FilterBypass fb, int off, String str, AttributeSet     attr)
                throws BadLocationException {
            fb.insertString(off, str.replaceAll("\\D++", ""), attr);  // remove non-digits
        }

        @Override
        public void replace(FilterBypass fb, int off, int len, String str, AttributeSet attr)
                throws BadLocationException {
            fb.replace(off, len, str.replaceAll("\\D++", ""), attr);  // remove non-digits
        }
    };
    ((AbstractDocument) field.getDocument()).setDocumentFilter(docFilter);
    ((AbstractDocument) field1.getDocument()).setDocumentFilter(docFilter);

}
public static void main(String[] args)
{
    final Sh s = new Sh();
    s.setDefaultCloseOperation(EXIT_ON_CLOSE);
    s.pack();
    s.setVisible(true);
   }
 }

text file: 5487


Answer:

your logic almost correct.but the main problem in your code you update textfield by a .txtfile before add document listener .you have to add document listner before you changed the textfield otherwise textfield not listen to document changes ..all you wanna do is move settext() code part to end of the document listener code

field.getDocument().addDocumentListener(docListener);
field1.getDocument().addDocumentListener(docListener);

///////////you should change text field after adding document listner not before//////////////////
/* move textfield settext code part like that*/
try {
    InputStream ips = new FileInputStream("test.txt");
    InputStreamReader ipsr = new InputStreamReader(ips);
    BufferedReader br = new BufferedReader(ipsr);
    String line;
    while ((line = br.readLine()) != null) {
        field.setText(line);
    }
    br.close();
} catch (Exception e) {
    e.printStackTrace();
}
//////////////////////////////////////////////////////////////////////////////////////

and alternative good solution....

you can share same document with both textfields so when you change one textfield another one also get changed .then you can't have different text in fields .you have a text in field1 .same text is in textfield 2.

field.setDocument(field1.getDocument());

Question:

I want two textfields (from now A and B) sharing the same content as the user inputs on any of them. I can make one mirror the other (B mirrors A) or the opposite (A mirrors B). But when I keep both DocumentListeners the execution starts to throw Exceptions.

According to the Oracle's Docs I can't use a DocumentListener to mutate the content of a document from within the Listener itself. Which I find weird since I already did it in the first case (B mirrors A) or the opposite case. Anyway the code still "works" but with an Exception thrown every two events triggered.

KeyListeners are not reliable for this particular case and I refuse to use buttons because I like the real-time look DocumentListener gives.

Any suggestions?

Here's my code:

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

public class Mirror {
    private JTextField oriText;
    private JTextField mirrorText;
    private static int debugCounter; //Counts the times an Event is Triggered.

    public static void main(String[] args) {
        Mirror gui = new Mirror();
        gui.build();
    }

    public void build(){

        JFrame frame = new JFrame();
        JPanel panel = new JPanel();
        panel.setLayout(new GridBagLayout());
        GridBagConstraints c = new GridBagConstraints();
        frame.getContentPane().add(panel);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        c.gridx = 0;
        c.gridy = 0;
        JLabel original = new JLabel("Original");
        panel.add(original, c);

        c.gridy = 1;
        oriText = new JTextField(10);
        panel.add(oriText,c);


        c.gridy = 2;
        JLabel mirror = new JLabel("Mirror");
        panel.add(mirror, c);

        c.gridy = 3;
        mirrorText = new JTextField(10);
        panel.add(mirrorText, c);           
        mirrorText.getDocument().addDocumentListener(new MyDocumentListenerII()); // Comment this line to see only the 1st Case (B mirrors A)
        oriText.getDocument().addDocumentListener(new MyDocumentListener()); // Comment this line to see only the 2nd Case (A mirrors B) 


        frame.pack();
        frame.setVisible(true);
    }

    class MyDocumentListener implements DocumentListener{

        @Override
        public void changedUpdate(DocumentEvent e) {
            //Does nothing.

        }

        @Override
        public void insertUpdate(DocumentEvent e) {
            mirror();
        }

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


    }

    class MyDocumentListenerII implements DocumentListener{

        @Override
        public void changedUpdate(DocumentEvent e) {
            // Does nothing.
        }

        @Override
        public void insertUpdate(DocumentEvent e) {
            mirror1();
        }

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

    }

    public void mirror(){
        if (!oriText.getText().equals(mirrorText.getText())){ //Without this each Event trigger the other in some sort of Paradoxical cycle. 
            mirrorText.setText(oriText.getText());
            debugCounter++;
            System.out.println(debugCounter+" events triggered");
        }
    }

    public void mirror1(){
        if (!mirrorText.getText().equals(oriText.getText())){
            oriText.setText(mirrorText.getText());
            debugCounter++;
            System.out.println(debugCounter+" events triggered");
        }
    }


}

Answer:

The problem you're having is that since both JTextFields need to be sync, each field's DocumentListener needs to update the other field. However, that update causes the other DocumentListener to attempt to update the first field, causing the thrown IllegalStateException, since something is attempting to modify the field while a DocumentListener for it is executing.

The solution is to block calls to setText(String) when a DocumentListener is being executed for that field. This can be done with boolean variables. Below is the code for one of the DocumentListeners:

textFieldA.getDocument().addDocumentListener(new DocumentListener() {
  @Override
  public void removeUpdate (DocumentEvent e) {
    blockA = true;
    if (!blockB) textFieldB.setText(textFieldA.getText());
    blockA = false;
  }
  @Override
  public void insertUpdate (DocumentEvent e) {
    blockA = true;
    if (!blockB) textFieldB.setText(textFieldA.getText());
    blockA = false;
  }
  @Override
  public void changedUpdate (DocumentEvent e) {
    blockA = true;
    if (!blockB) textFieldB.setText(textFieldA.getText());
    blockA = false;
  }
});

where blockA and blockB are boolean instance fields (or possibly final variables in the method). Now, when a method fires in textFieldA's DocumentListener, a flag is set to indicate not to use textFieldA.setText(String). We, also see how textFieldB.setText(String) is blocked when blockB is set. The DocumentListener for textFieldB looks about the same.

Now, textFieldA won't be set during a call inside its DocumentListener, same for the other field. Such a call would be redundant anyway: the text of each field would be same at that point.