javasortingtablerowsorter

TableRowSorter returning incorrect SortOrder


I have made a TableRowSorter which should update the cycle in which each column is sorted from ASCENDING => DESCENDING => ASCENDING => DESCENDING => ... to ASCENDING => DESCENDING => UNSORTED => ASCENDING => DESCENDING => UNSORTED => ...

import java.util.ArrayList;
import java.util.List;

import javax.swing.SortOrder;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;

public class ADU_SortOrder<M extends TableModel> extends TableRowSorter<M> {

    public ADU_SortOrder(M model) {
        setModel(model);
    }

    @Override
    public void toggleSortOrder(int column) {
        List<? extends SortKey> sortKeys = getSortKeys();
        if (sortKeys.size() > 0) {
            List<SortKey> keys = new ArrayList<SortKey>(getSortKeys());
            keys.set(0, new SortKey(column, setNextOrder(sortKeys.get(0).getSortOrder())));
            setSortKeys(keys);
            return;
        }
        super.toggleSortOrder(column);
    }

    private SortOrder setNextOrder(SortOrder order) {
        switch (order) {
            case ASCENDING:
                return SortOrder.DESCENDING;
            case DESCENDING:
                return SortOrder.UNSORTED;
            case UNSORTED:
                return SortOrder.ASCENDING;
            default:
                return SortOrder.UNSORTED;
        }
    }
}

In the given example I will try to describe what I expect to happen and what actually happens. You have a JTable with two columns. The SortKeys which are used at the start make it so that the last sort in each column were so that the columns were SortOrder.ASCENDING

What I expect would happen here would be the following.

Sort Order in: Column 1           Column 2
Start:        ASCENDING           ASCENDING
Click C1:     DESCENDING          ASCENDING
Click C1:      UNSORTED           ASCENDING
Click C2:      UNSORTED          DESCENDING
Click C1:     ASCENDING          DESCENDING
Click C2:     ASCENDING           UNSORTED

What actually happens:

Sort Order in: Column 1           Column 2
Start:        ASCENDING           ASCENDING
Click C1:     DESCENDING          ASCENDING
Click C1:      UNSORTED           ASCENDING
Click C2:      UNSORTED           ASCENDING
Click C1:     DESCENDING          ASCENDING
Click C2:     DESCENDING          UNSORTED

As you can see instead of getting the sort order of the column clicked, the TableRowSorter gets the last sort order applied and then just applies the next sort order to the next column clicked. How would I be able to treat each column individually and update the columns so that it worked as expected?


Here is a MCV. I apologise for not including one previously.

import java.awt.Dimension;
import java.awt.GridLayout;
import java.util.ArrayList;
import java.util.List;

import javax.swing.SortOrder;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.RowSorter.SortKey;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;

public class MVC extends JFrame {
    private static final long serialVersionUID = -8809862571237032846L;

    private MVC() {
        setTitle("MVC");

        createAndRunGUI();

        pack();
        setVisible(true);
        setLocationRelativeTo(null);
    }

    private void createAndRunGUI() {
        setLayout(new GridLayout(1, 1));

        String[] tableHeaders = {"Col 1", "Col 2"};
        String[][] tableData = {
                {"Hi", "Animals"},
                {"Bob", "Of"},
                {"Phil", "The"},
                {"Dog", "World"},
                {"Cat", "Include"},
                {"Pill", "Fish"},
                {"Dab", "Ants"},
                {"Bob", "Hippos"},
                {"Fill", "Humans"},
                {"Space", "TVs"},
                {"Up", "Is"},
                {"Now", "That"},
                {"Even", "Right"},
                {"More", "Person"},
                {"Data", "?"}
            };

        JTable table = new JTable(tableData, tableHeaders);
        table.setAutoCreateRowSorter(true);
        ADU_SortOrder<TableModel> tableSorter = new ADU_SortOrder<TableModel>(table.getModel());
        List<SortKey> keys = new ArrayList<SortKey>();
        //keys.add(new RowSorter.SortKey( 0, SortOrder.ASCENDING));
        //keys.add(new RowSorter.SortKey( 1, SortOrder.ASCENDING));
        tableSorter.setSortKeys(keys);
        table.setRowSorter(tableSorter);

        JScrollPane myScrollTable = new JScrollPane(table);
        myScrollTable.setPreferredSize(new Dimension(600, 600));

        JPanel cont = new JPanel();
        cont.add(myScrollTable);

        getContentPane().add(cont);
    }

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

    private class ADU_SortOrder<M extends TableModel> extends TableRowSorter<M> {
        public ADU_SortOrder(M model) {
            setModel(model);
        }

        @Override
        public void toggleSortOrder(int column) {
            List<? extends SortKey> sortKeys = getSortKeys();
            if (sortKeys.size() > 0) {
                List<SortKey> keys = new ArrayList<SortKey>(getSortKeys());
                keys.set(0, new SortKey(column, setNextOrder(sortKeys.get(0).getSortOrder())));
                setSortKeys(keys);
                return;
            }
            super.toggleSortOrder(column);
        }

        private SortOrder setNextOrder(SortOrder order) {
            switch (order) {
                case ASCENDING:
                    return SortOrder.DESCENDING;
                case DESCENDING:
                    return SortOrder.UNSORTED;
                case UNSORTED:
                    return SortOrder.ASCENDING;
                default:
                    return SortOrder.UNSORTED;
            }
        }
    }
}

Edit 2
Looking at the specific method toggleSortOrder whilst the program is running it becomes apparent that the problem is with the part of code which says setNextOrder(keys.get(0).getSortOrder()). This is because when the code returns getSortKeys() as an ArrayList the size of the list is only ever 0 or 1. This means that when the length of the list is 1 the code get(0) returns the last column sorted but the code get(column) would cause an IndexOutOfBoundsException. So how can I allow the program to get the SortKeys for every column?

@Override
public void toggleSortOrder(int column) {
    System.out.println("Column: " + column);
    List<SortKey> keys = new ArrayList<SortKey>(getSortKeys());
    System.out.println("List Size: " + keys.size());
    if (keys.size() > 0) {
        keys.set(0, new SortKey(column, setNextOrder(keys.get(0).getSortOrder())));
        setSortKeys(keys);
        return;
    }
    super.toggleSortOrder(column);
}

Edit 3

Using Andreas' suggestion I created a way to be able to use keys.set(column, new SortKey(column, setNextOrder(keys.get(column).getSortOrder())));

List<SortKey> keys = new ArrayList<SortKey>();

@Override
public void toggleSortOrder(int column) {
    System.out.println("Column: " + column);
    System.out.println("List Size: " + keys.size());
    if (keys.size() > 0) {
        keys.set(column, new SortKey(column, setNextOrder(keys.get(column).getSortOrder())));
        setSortKeys(keys);
        return;
    }
    super.toggleSortOrder(column);
}

private SortOrder setNextOrder(SortOrder order) {
    switch (order) {
        case ASCENDING:
            return SortOrder.DESCENDING;
        case DESCENDING:
            return SortOrder.UNSORTED;
        case UNSORTED:
            return SortOrder.ASCENDING;
        default:
            return SortOrder.UNSORTED;
    }
}

private void initialSortOrder(M model) {
    for(int i = 0; i < model.getColumnCount(); i++) {
        keys.add(new RowSorter.SortKey( i, SortOrder.ASCENDING));
    }
}

However using this method column 1 can be sorted but only the words Of and Hippo can be sorted in the second column. I know this has turned into a very long question and sorry but I do appreciate any help


Solution

  • In order to fix this, I needed to add a few more if statements and one or two variables.

    import java.util.ArrayList;
    import java.util.List;
    
    import javax.swing.SortOrder;
    import javax.swing.table.TableModel;
    import javax.swing.table.TableRowSorter;
    
    public class ADU_SortOrder<M extends TableModel> extends TableRowSorter<M> {
        public ADU_SortOrder(M model) {
            setModel(model);
        }
    
        boolean firstTime = true; //Needed in case there are any initial sort keys
        int columnHolder = -1;
    
        @Override
        public void toggleSortOrder(int column) {
            List<? extends SortKey> sortKeys = getSortKeys();
            if(sortKeys.size() == 0) { //For if there are no initial sort keys
                List<SortKey> keys = new ArrayList<SortKey>();
                keys.add(new SortKey(column, SortOrder.ASCENDING));
                setSortKeys(keys);
                return;
            }
    
            if (sortKeys.size() > 0 && columnHolder == column || firstTime) {
                if(firstTime) {
                    firstTime = false;
                    columnHolder = column;
                    if(column != sortKeys.get(0).getColumn()) {
                        List<SortKey> keys = new ArrayList<SortKey>(getSortKeys());
                        keys.set(0, new SortKey(column, SortOrder.ASCENDING));
                        setSortKeys(keys);
                        return;
                    }
                }
    
                List<SortKey> keys = new ArrayList<SortKey>(getSortKeys());
                keys.set(0, new SortKey(column, setNextOrder(sortKeys.get(0).getSortOrder())));
                setSortKeys(keys);
                return;
            } else if(sortKeys.size() > 0 && columnHolder != column && !firstTime) {
                List<SortKey> keys = new ArrayList<SortKey>(getSortKeys());
                keys.set(0, new SortKey(column, SortOrder.ASCENDING));
                setSortKeys(keys);
                columnHolder = column;
                return;
            }
            super.toggleSortOrder(column);
        }
    
        private SortOrder setNextOrder(SortOrder order) {
            switch (order) {
                case ASCENDING:
                    return SortOrder.DESCENDING;
                case DESCENDING:
                    return SortOrder.UNSORTED;
                case UNSORTED:
                    return SortOrder.ASCENDING;
                default:
                    return SortOrder.UNSORTED;
            }
        }
    }