Search code examples
javaswingjtablerowfilter

"Concatenating" andFilter and orFilter for RowFilter


I have a JTable with four columns, the first one containing either a number or a text, the other three only text. I'm trying to filter this table with the help of a RowFilter:

sorter = new TableRowSorter<TableModel>(myOwnTableModel);

The checkboxFilter I got works well enough:

sorter.setRowFilter(RowFilter.regexFilter("^[0-9]$", 0));

This sorter is activated or deactivate depending on a checkbox that is either set or not.
The second filtering happens if a user puts some text in a textfield. For itself, this works fine already:

String regex = "(?i)" + Pattern.quote(s); // s = input Text of user
sorter.setRowFilter(RowFilter.regexFilter(regex, 1,2,3));

What I can't do, is to activate both filters at the same time. Maybe I'm thinking way too far, my idea has been to "concatenate" the two filters, the checkboxFilter should be "and" the other "or". I tried several things, to me the most promising looked something like:

String regex = "(?i)" + Pattern.quote(s);
bookFilter = RowFilter.regexFilter(regex, 1,2,3);
sorter.setRowFilter(bookFilter.andFilter(Arrays.asList(
                    RowFilter.regexFilter("^[0-9]$", 0))));

Unfortunately, this doesn't lead to any usable result. Any ideas appreciated :)


Solution

  • The solution is to add an ActionListener to the JCheckBox to update the filter state if the checkbox is toggled and to add a DocumentListener to the JTextField's underlying Document to update the filter state if the contents of the field is updated.

    The other bug in your code is that you are calling the static andFilter method on your bookFilter instance and are only passing in the newly constructed regex filter (i.e. you are only passing in one parameter to andFilter). The correct usage is:

    RowFilter andFilter = RowFilter.andFilter(filter1, filter2, etc);
    

    Example Event Listeners

    JCheckBox cb = ...
    cb.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent evt) {
        updateFilters();
      }
    });
    
    JTextField tf = ...
    tf.getDocument().addDocumentListener(new DocumentListener() {
      public void insertUpdate(DocumentEvent e) { updateFilters(); }
      public void removeUpdate(DocumentEvent e) { updateFilters(); }
      publci void changedUpdate(DocumentEvent e) { updateFilters(); }
    });
    

    ... and then define your updateFilters() method to install a new filter based on when the checkbox is selected and whether the text field is empty or not.

    Example Filter Update Method

    public void updateFilters() {
      if (cb.isSelected()) {
        if (tf.getText().length() > 0) {
           // Both filters active so construct an and filter.
           sorter.setRowFilter(RowFilter.andFilter(bookFilter, checkBoxFilter));
        } else {
           // Checkbox selected but text field empty.
           sorter.setRowFilter(checkBoxFilter);
        }
      } else if (tf.getText().length() > 0) {
        // Checkbox deselected but text field non-empty.
        sorter.setRowFilter(bookFilter);
      } else {
        // Neither filter "active" so remove filter from sorter.
        sorter.setRowFilter(null); // Will cause table to re-filter.
      }
    }