Using DocumentFilter.FilterBypass
Asked Answered
M

2

2

I want to have a method like this on my DocumentFilter

public void replaceUpdate(int offset, int length, String text) {
        try {
            super.replace(byPass, offset, length, text, null);
        } catch (BadLocationException ex) {
            //error
        }
}

Currently in order to get an instance of FilterBypass (byPass on method above) , I need to get from the overridden method insertString :

private FilterBypass byPass;

@Override
    public void insertString(DocumentFilter.FilterBypass fb,
            int offset, String string, AttributeSet att)
            throws BadLocationException {
        byPass = fb;
        //some stuff here
        super.insertString(fb, offset, string, att);
    }

But this is causing me some problems. Can anyone suggest some different way of getting a FilterBypass? I can't find a way to get a reference to the FilterBypass differently.

If I was to override its methods how should it be?

Marquis answered 19/2, 2012 at 1:13 Comment(5)
I strongly suggest that you create and post an sscce (please have a look at the link!) that shows your problem. This code is small, compiles for us, runs for us, has no extraneous code unrelated to your problem and demonstrates the problem for all.Sudra
I believe I state clearly that I need a different way to get the FilterBypass since this way is causing problems.Marquis
The only way I know of to get the relevant FilterBypass object is via the one given by the JVM when a DocumentFilter method override is called. And similar to the Graphics object and the paint/paintComponent method, I believe that this object does not persist between method calls. I guess I'm confused as to your purpose. You seem to be wanting to call the filter's methods in a non-event driven way. When would your replaceUpdate be called? Would it be from within one of the DocumentFilter overridden methods?Sudra
The overridden methods are called when the user is typing and input is "filtered". The replaceUpdate is called from external source. I do not want the external source to be "filtered" and I just want it to go into the document. Thats why I have replaceUpdate.Marquis
What about if the text is added by the external source and then more text is added by the user? How do you distinguish what is entered by the user and what is entered by the external source? I have a feeling that your solution is not to get the FilterBypass in some unusual way, but by altering the logic of your DocumentFilter's methods, but I can't recommend more without knowing more. Again, an sscce I believe would help greatly.Sudra
S
10

For instance, here's an SSCCE with a DocumentFilter which prevents the user from typing numbers into the document but allows the Swing Timer to do so.

import java.awt.event.*;
import java.awt.event.ActionListener;
import javax.swing.*;
import javax.swing.text.*;

public class DocFilterPanel extends JPanel {
   private JTextArea textArea = new JTextArea(12, 50);
   private MyDocFilter myDocFilter = new MyDocFilter();

   public DocFilterPanel() {
      ((PlainDocument) textArea.getDocument()).setDocumentFilter(myDocFilter);
      int vsbPolicy = JScrollPane.VERTICAL_SCROLLBAR_ALWAYS;
      int hsbPolicy = JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED;
      add(new JScrollPane(textArea, vsbPolicy , hsbPolicy));

      int timerDelay = 1000;
      new Timer(timerDelay , new ActionListener() {

         @Override
         public void actionPerformed(ActionEvent e) {
            myDocFilter.setFilter(false);
            textArea.append("12345\n");
            myDocFilter.setFilter(true);
         }
      }).start();
   }

   private static void createAndShowGui() {
      DocFilterPanel docFilterPanel = new DocFilterPanel();

      JFrame frame = new JFrame("DocFilterTest");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(docFilterPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

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

class MyDocFilter extends DocumentFilter {
   private static final String REMOVE_REGEX = "\\d";
   private boolean filter = true;

   public boolean isFilter() {
      return filter;
   }

   public void setFilter(boolean filter) {
      this.filter = filter;
   }

   @Override
   public void insertString(FilterBypass fb, int offset, String text,
         AttributeSet attr) throws BadLocationException {
      if (filter) {
         text = text.replaceAll(REMOVE_REGEX, "");
      }
      super.insertString(fb, offset, text, attr);

   }

   @Override
   public void replace(FilterBypass fb, int offset, int length, String text,
         AttributeSet attrs) throws BadLocationException {
      if (filter) {
         text = text.replaceAll(REMOVE_REGEX, "");
      }
      super.replace(fb, offset, length, text, attrs);

   }
}
Sudra answered 19/2, 2012 at 3:22 Comment(3)
I believed there would be problems with concurrency if I used the flag, but after trying it, it looks like there aren't. This also looks better than my solution. tyMarquis
@latusaki: I was worried about that too. I suppose you could always disable the JTextField while adding text from the program and then re-enable it.Sudra
I am not sure if it makes any difference but I am using SwingUtilities.invokeLater to make the call to the DocumentFilter. In any case I haven't had a problem so far hopefully I won't have in the future ether.Marquis
D
0

I liked @hovercraft's solution, but it gave me concurrency issues. I solved this by synchronizing the filter toggling on the text area. Like this:

synchronized (textArea) {
    myDocFilter.setFilter(false);
    textArea.append("12345\n");
    myDocFilter.setFilter(true);
}
Darryl answered 6/3, 2014 at 14:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.