/* * SortableTableRowSorter.java * Copyright 2016 Connor Petty <cpmeister@users.sourceforge.net> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Created on Jan 17, 2016, 1:10:22 PM */ package pcgen.gui2.util.table; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; import javax.swing.RowSorter; import javax.swing.SortOrder; import pcgen.util.Comparators; /** * * @author Connor Petty <cpmeister@users.sourceforge.net> */ public class SortableTableRowSorter extends RowSorter<SortableTableModel> { private List<? extends RowSorter.SortKey> sortKeys = Collections.emptyList(); private SortableTableModel model; public SortableTableRowSorter() { this(null); } public SortableTableRowSorter(SortableTableModel model) { setModel(model); } @Override public SortableTableModel getModel() { return model; } public void setModel(SortableTableModel model) { this.model = model; } /** * Reverses the sort order from ascending to descending (or descending * to ascending) if the specified column is already the primary sorted * column; otherwise, makes the specified column the primary sorted * column, with an ascending sort order. If the specified column is not * sortable, this method has no effect. * * @param column index of the column to make the primary sorted column, * in terms of the underlying model * @throws IndexOutOfBoundsException {@inheritDoc} */ @Override public void toggleSortOrder(int column) { List<RowSorter.SortKey> keys = new ArrayList<>(getSortKeys()); RowSorter.SortKey sortKey; int sortIndex; for (sortIndex = keys.size() - 1; sortIndex >= 0; sortIndex--) { if (keys.get(sortIndex).getColumn() == column) { break; } } if (sortIndex == -1) { // Key doesn't exist sortKey = new RowSorter.SortKey(column, SortOrder.ASCENDING); keys.add(0, sortKey); } else if (sortIndex == 0) { // It's the primary sorting key, toggle it keys.set(0, toggle(keys.get(0))); } else { // It's not the first, but was sorted on, remove old // entry, insert as first with ascending. keys.remove(sortIndex); keys.add(0, new RowSorter.SortKey(column, SortOrder.ASCENDING)); } if (keys.size() > 2) { keys = keys.subList(0, 2); } setSortKeys(keys); } private RowSorter.SortKey toggle(RowSorter.SortKey key) { if (key.getSortOrder() == SortOrder.ASCENDING) { return new RowSorter.SortKey(key.getColumn(), SortOrder.DESCENDING); } return new RowSorter.SortKey(key.getColumn(), SortOrder.ASCENDING); } @Override public int convertRowIndexToModel(int index) { return index; } @Override public int convertRowIndexToView(int index) { return index; } @Override public void setSortKeys(List<? extends RowSorter.SortKey> keys) { sortKeys = keys; sort(); } private void sort() { SortableTableModel m = getModel(); if (m == null) { return; } int columnCount = m.getColumnCount(); Comparator[] comparators = new Comparator[columnCount]; for (int i = 0; i < columnCount; i++) { comparators[i] = Comparators.getComparatorFor(m.getColumnClass(i)); } int keyCount = sortKeys.size(); RowSorter.SortKey[] keys = sortKeys.toArray(new RowSorter.SortKey[keyCount]); m.sortModel(new RowComparator(keys, comparators)); } @Override public List<? extends RowSorter.SortKey> getSortKeys() { return sortKeys; } @Override public int getViewRowCount() { if (getModel() == null) { return 0; } return getModel().getRowCount(); } @Override public int getModelRowCount() { if (getModel() == null) { return 0; } return getModel().getRowCount(); } @Override public void modelStructureChanged() { } @Override public void allRowsChanged() { } @Override public void rowsInserted(int firstRow, int endRow) { } @Override public void rowsDeleted(int firstRow, int endRow) { } @Override public void rowsUpdated(int firstRow, int endRow) { } @Override public void rowsUpdated(int firstRow, int endRow, int column) { } } class RowComparator implements Comparator<Row> { private final RowSorter.SortKey[] keys; private final Comparator[] comparators; public RowComparator(RowSorter.SortKey[] keys, Comparator[] comparators) { this.keys = keys; this.comparators = comparators; } @Override public int compare(Row o1, Row o2) { for (RowSorter.SortKey key : keys) { if (key.getSortOrder() == SortOrder.UNSORTED) { continue; } int column = key.getColumn(); Comparator comparator = comparators[column]; Object obj1 = o1.getValueAt(column); Object obj2 = o2.getValueAt(column); int ret; if (obj1 == null) { if (obj2 == null) { ret = 0; } else { ret = -1; } } else if (obj2 == null) { ret = 1; } else { ret = comparator.compare(obj1, obj2); } if (key.getSortOrder() == SortOrder.DESCENDING) { ret *= -1; } if (ret != 0) { return ret; } } return 0; } @Override public int hashCode() { int hash = 7; hash = 19 * hash + Arrays.deepHashCode(this.keys); hash = 19 * hash + Arrays.deepHashCode(this.comparators); return hash; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final RowComparator other = (RowComparator) obj; if (!Arrays.deepEquals(this.keys, other.keys)) { return false; } if (!Arrays.deepEquals(this.comparators, other.comparators)) { return false; } return true; } }