How to Extend TableRowSorter to change only a single column's comparator, have super handle the rest

6.5k views Asked by At

Hi I am trying to use a custom comparator for only a single column in the JTable. I have accomplished this with the following code. The problem is that doing this breaks the appropriate sort for Integer.

I have a JTable where the column classes are: String | Integer | Integer | Boolean | Boolean

The Comparators I'm using are these. The first uses a split from How to split a string between letters and digits (or between digits and letters)?.

//compare strings being aware of numbers embedded in the string
private static Comparator<String> numberAwareCompare = new Comparator<String>() {
    public int compare(String s1, String s2) {

        String[] s1Parts = s1.split("(?<=\\D)(?=\\d)|(?<=\\d)(?=\\D)");
        String[] s2Parts = s2.split("(?<=\\D)(?=\\d)|(?<=\\d)(?=\\D)");

        int i = 0;
        while(i < s1Parts.length && i < s2Parts.length){

            //if parts are the same
            if(s1Parts[i].compareTo(s2Parts[i]) == 0){
                ++i;
            }else{
                try{

                    int intS1 = Integer.parseInt(s1Parts[i]);
                    int intS2 = Integer.parseInt(s2Parts[i]);

                    //if the parse works

                    int diff = intS1 - intS2; 
                    if(diff == 0){
                        ++i;
                    }else{
                        return diff;
                    }
                }catch(Exception ex){
                    return s1.compareTo(s2);
                }
            }//end else
        }//end while

            //Handle if one string is a prefix of the other.
        if(s1.length() < s2.length()){
            return -1;
        }else if(s1.length() > s2.length()){
            return 1;
        }else{
            return 0;
        }

        return 0;
    }
};

I extend the TableRowSorter as follows:

public class FeatureRowSorter extends TableRowSorter<TableModel>{
    public FeatureRowSorter(TableModel tableModel) {
        super(tableModel);
        //setComparator(0, numberAwareCompare);
        //setComparator(1,intCompare);
        //setComparator(2,intCompare);
    }

    @Override
    public Comparator<?> getComparator(int column){
        if(column == 0){
            return numberAwareCompare;
        }else{
            return super.getComparator(column);
        }
    }
}

This results in the following undesired sort behavior: enter image description here

This can be overcome by creating an Integer sort. This is the issue. I should not have to create a sort for Integer. If the default TableRowSorter knows the class is Integer why can it not create it?

So I create a trivial Integer sorter as follows:

//Integer compare, need this to use the custom TableRowSorter
public static Comparator<Integer> intCompare = new Comparator<Integer>() {
    @Override
    public int compare(Integer o1, Integer o2) {
        return o1 - o2;
    }
};

Now I delete the getComparator method above and explictly add the comparators to the correct columns. (The commented out code above). This has the correct sorting behavior.

I add the row sorter to the table with the following code. My model creates a new custom row sorter and sends it to the view's JTable in a controller object.

//In the model, FeatureTableModel extends DefaultTableModel,getClass method implemented correctly    
public FeatureRowSorter getFeatureRowSorter(){
        return new FeatureRowSorter(getFeatureTableModel());
    }

Then add the sorter to the table like so:

view.getFeatureTable().setRowSorter(model.getFeatureRowSorter());

So the question is, how would I do this with out having to implement the Integer sorter? This seems like a hack, and the fact that I'm implementing an Integer comparator seems fishy. The TableRowSorter knows those columns are Integer because of the FeatureTableModel's getClass method which is implemented correctly. (Nevermind that one of the boolean columns is not checkbox. I'm overriding that renderer).

Any suggestion would be appreciated.

UPDATE:

After reading Duncan's answer I found I had not imiplemented getClass correctly. I had:

@Override
    public Class<?> getColumnClass(int column){
        if(column == 4){
            return Boolean.class;
        }else if(column == 2){
            return Integer.class;
        }else{
            return Object.class;
        }
    }

I changed this to:

@Override
    public Class<?> getColumnClass(int column){
        if(column == 4){
            return Boolean.class;
        }else if(column == 1 || column == 2){
            return Integer.class;
        }else{
            return Object.class;
        }
    }
1

There are 1 answers

0
Duncan Jones On BEST ANSWER

Based on a quick bit of testing, I achieved the desired results by calling the setComparator method on the TableRowSorter instance. That is, I didn't create my own class that extends TableRowSorter.

JTable table = new JTable(model);

TableRowSorter<TableModel> rowSorter = new TableRowSorter<TableModel>(model);
table.setRowSorter(rowSorter);

rowSorter.setComparator(0, new Comparator<String>() {
  @Override
  public int compare(String o1, String o2) {
    // etc.
  }

});

In my test, this approach had no negative affects on the ability to sort a column of integers in the same table.

I appreciate this isn't an explanation of why you are encountering problems, but it is worth a shot as a potentially simpler work-around.