Question

I have a JTable which uses a RowFilter to filter fields but i want those results to be narrowed down to x amount like e.g. 5

Was it helpful?

Solution

This is a lot of fun.

Basically, there is no way for the RowFilter to know where the RowSorter will want to check for included lines, so you can't place some kind of "counter" in the RowFilter and simply start returning false after a predetermined limit. The RowSorter also makes no guarantee that it will actually look for included lines in any order or as a whole group...it could check random lines, for example...

What you can do though, is create a RowSorter of your own and override it's sort method, which generally gets called whenever the underlying model has changed in some way or the model reference itself changes. Here you can then tell the RowFilter that it needs to reset it's count.

The Row Sorter

Basically, this is an implementation of TableRowSorter that looks for a special type of RowFilter and calls its reset method whenever it is called, this is meant to reset the number of lines that have been filtered back to 0

public class MyRowSorter extends TableRowSorter<TableModel> {

    public MyRowSorter(TableModel model) {
        super(model);
    }

    @Override
    public void sort() {
        RowFilter<? super TableModel, ? super Integer> filter = getRowFilter();
        if (filter instanceof LimitedRowFilter) {
            LimitedRowFilter lrf = (LimitedRowFilter) filter;
            lrf.reset();
        }
        super.sort(); 
    }

}

The LimitedRowFilter

This is a special, base, RowFilter which provides a lineCount and lineLimit which can be used by implementations to check if the number of filtered lines exceeds the maximum number of allowed lines.

It also provides the reset method used by the RowSorter

This could use some more additional functionality like includeAndIncrement which could return true if the line should be included or false if it would exceed the line count limit and would increment the lineCount automatically...

public abstract class LimitedRowFilter<M, I> extends RowFilter<M, I> {

    private int lineLimit;
    private int lineCount;

    public void reset() {
        lineCount = 0;
    }

    public int getLineCount() {
        return lineCount;
    }

    public void incrementLineCount() {
        lineCount++;
    }

    public int getLineLimit() {
        return lineLimit;
    }

    public void setLineLimit(int lineLimit) {
        this.lineLimit = lineLimit;
    }

    public LimitedRowFilter(int lineLimit) {
        this.lineLimit = lineLimit;
    }

    public LimitedRowFilter() {
    }

}

An example implementation of a LimitedRowFilter

This is a simple example of the LimitedRowFilter in action, it basically will return true for each line up to the maximum allowed limit...

public class MyRowFilter extends LimitedRowFilter<TableModel, Integer> {

    public MyRowFilter() {
        super();
    }

    public MyRowFilter(int limit) {
        super(limit);
    }

    @Override
    public boolean include(Entry<? extends TableModel, ? extends Integer> entry) {

        boolean included = true;
        // Do you own checking here to determine if the row should be included or
        // not
        if (included) {
            if (getLineCount() < getLineLimit()) {
                incrementLineCount();
            } else {
                included = false;
            }
        }

        return included;

    }

}

A runnable example

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import javax.swing.DefaultRowSorter;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.RowFilter;
import javax.swing.RowSorter;
import javax.swing.SortOrder;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;

public class LimitedTableRowFilter {

    public static void main(String[] args) {
        new LimitedTableRowFilter();
    }

    public LimitedTableRowFilter() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }
                DefaultTableModel model = new DefaultTableModel(new Object[]{"A"}, 0);
                for (int index = 0; index < 100; index++) {
                    model.addRow(new Object[]{index});
                }
                JTable table = new JTable(model);
                MyRowSorter sorter = new MyRowSorter(model);
                MyRowFilter filter = new MyRowFilter(10);

                sorter.setRowFilter(filter);
                table.setRowSorter(sorter);

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new JScrollPane(table));
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }
}

Disclaimer

I'll be the first to admit that this is a less than perfect solution, but it beats having to copy the entire source for the DefaultRowSorter and TableRowSorter just so we can gain access to the private methods we would need to use to implement the functionality directly within the sorter itself.

I've only done limited testing so you may need to do some of your own tweaking...

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top