001package jmri.swing;
002
003import java.util.ArrayList;
004import java.util.List;
005import java.util.Objects;
006import javax.annotation.Nonnull;
007import javax.swing.RowSorter;
008import javax.swing.RowSorter.SortKey;
009import javax.swing.SortOrder;
010import javax.swing.event.RowSorterEvent;
011import javax.swing.event.RowSorterListener;
012import javax.swing.table.TableModel;
013
014/**
015 * Utilities for handling JTable row sorting, assuming only a single column
016 * influences the table sort order.
017 * <p>
018 * Multi-column sorting should be controlled by directly manipulating the
019 * {@link javax.swing.RowSorter.SortKey}s returned by
020 * {@link javax.swing.RowSorter#getSortKeys()}.
021 *
022 * @author Randall Wood
023 */
024public final class RowSorterUtil {
025
026    /**
027     * Get the sort order for a column given a RowSorter for the TableModel
028     * containing the column.
029     *
030     * @param rowSorter the sorter
031     * @param column    the column index in the model, not the view
032     * @return the sort order or {@link javax.swing.SortOrder#UNSORTED}.
033     */
034    @Nonnull
035    public static SortOrder getSortOrder(@Nonnull RowSorter<? extends TableModel> rowSorter, int column) {
036        for (SortKey key : rowSorter.getSortKeys()) {
037            if (key.getColumn() == column) {
038                return key.getSortOrder();
039            }
040        }
041        return SortOrder.UNSORTED;
042    }
043
044    /**
045     * Set the sort order for a table using the specified column given a
046     * RowSorter for the TableModel containing the column.
047     * <p>
048     * This makes all other columns unsorted, even if the specified column is
049     * also specified to be unsorted.
050     *
051     * @param rowSorter the sorter
052     * @param column    the column index in the model, not the view
053     * @param sortOrder the sort order
054     */
055    public static void setSortOrder(@Nonnull RowSorter<? extends TableModel> rowSorter, int column, @Nonnull SortOrder sortOrder) {
056        List<SortKey> keys = new ArrayList<>();
057        if (!sortOrder.equals(SortOrder.UNSORTED)) {
058            keys.add(new RowSorter.SortKey(column, sortOrder));
059        }
060        rowSorter.setSortKeys(keys);
061    }
062
063    /**
064     * Add a RowSorterListener to the rowSorter that prevents multiple columns
065     * from being considered while sorting.
066     *
067     * @param rowSorter the sorter to add the listener to
068     * @return the added listener
069     * @throws NullPointerException if rowSorter is null
070     */
071    public static RowSorterListener addSingleSortableColumnListener(@Nonnull RowSorter<? extends TableModel> rowSorter) {
072        Objects.requireNonNull(rowSorter, "rowSorter must be nonnull.");
073        RowSorterListener listener = new RowSorterListener() {
074            List<? extends SortKey> priorSortKeys = new ArrayList<>();
075
076            @Override
077            public void sorterChanged(RowSorterEvent e) {
078                if (e.getType().equals(RowSorterEvent.Type.SORT_ORDER_CHANGED)) {
079                    RowSorter<?> source = e.getSource();
080                    List<? extends SortKey> newSortKeys = new ArrayList<>(source.getSortKeys());
081                    newSortKeys.removeAll(priorSortKeys);
082                    if (!newSortKeys.isEmpty()) {
083                        priorSortKeys = newSortKeys;
084                        source.setSortKeys(priorSortKeys);
085                        source.allRowsChanged();
086                    }
087                }
088            }
089        };
090        rowSorter.addRowSorterListener(listener);
091        return listener;
092    }
093}