All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.cuf.model.ui.CufTableRowSorter Maven / Gradle / Ivy

The newest version!
package net.sf.cuf.model.ui;

import javax.swing.JTable;
import javax.swing.SortOrder;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

/**
 * RowSorter that allows switching to {@link SortOrder#UNSORTED} in addition what a
 * {@link TableRowSorter}, already does, like taking into consideration filters like CoderazziFilter.
 * 

* The public methods are based on the now deprecated NewTableSorter, which was a TableModel, to make migration to this sorter easier. *

* In contrast to the NewTableSorter, selection is not lost/mistaken when sorting and using filter at the same time. */ public class CufTableRowSorter extends TableRowSorter { /** Standard comparator used for classes that implements Comparable interface */ public static final Comparator> COMPARABLE_COMPARATOR = new ComparableComparator(); /** You can sort by max 3 columns */ private final static int MAX_SORT_KEYS = 3; /** * "-1" safe helper method to convert from a view index to a model index. * @param pTable the table, must not be null * @param pViewIndex -1 or the view index * @return -1 or the model index */ public static int convertRowIndexToModel(JTable pTable, int pViewIndex) { if (pViewIndex<0) return pViewIndex; else if (pViewIndex >= pTable.getRowCount()) return -1; // map bad indices to -1 else return pTable.convertRowIndexToModel(pViewIndex); } /** * "-1" safe helper method to convert from a model index to a view index. * @param pTable the table, must not be null * @param pModelIndex -1 or the model index * @return -1 or the view index */ public static int convertRowIndexToView(JTable pTable, int pModelIndex) { if (pModelIndex<0) return pModelIndex; else if (pModelIndex >= pTable.getModel().getRowCount()) return -1; // map bad indices to -1 else return pTable.convertRowIndexToView(pModelIndex); } /** * Creates a CufTableRowSorter using pModel * as the underlying TableModel. We assume that all columns are sortable. * * @param pModel the underlying TableModel to use, * null is treated as an empty model */ public CufTableRowSorter(M pModel) { super(pModel); for (int i=0; i< getColumnCount(); i++) { setSortable(i, true); } setMaxSortKeys(MAX_SORT_KEYS); } /** * Sets comparator of given column in table * @param pColumn column to be set with comparator * @param pComparator comparator */ public void setColumnComparator(final int pColumn, final Comparator pComparator) { setComparator(pColumn, pComparator); } /** * Adds given comparator to all columns of particular type (class) * @param pType column data type (class) * @param pComparator comparator */ public void setColumnComparator(final Class pType, final Comparator pComparator) { for(int pColumn=0; pColumn< getColumnCount(); pColumn++) { if (getColumnClass(pColumn) == pType) { setComparator(pColumn, pComparator); } } } /** * Check if the given cell is editable. * @param pRow the row index * @param pColumn the column index * @return true if the cell can be edited */ public boolean isCellEditable(final int pRow, final int pColumn) { return getModelWrapper().getModel().isCellEditable(pRow, pColumn); } /** * Get the value of the given cell. * @param pRow the row index * @param pColumn the column index * @return the current value of the cell */ public Object getValueAt(final int pRow, final int pColumn) { return getModelWrapper().getValueAt(pRow, pColumn); } /** * Sets value on given cell. * @param pValue value to set * @param pRow row index * @param pColumn column index */ public void setValueAt(final Object pValue, final int pRow, final int pColumn) { getModelWrapper().getModel().setValueAt(pValue, pRow, pColumn); } /** * Forces initial sorting (ASCENDING) of given column * @param pColumnForInitialSorting column ID */ public void forceInitialSorting(int pColumnForInitialSorting) { toggleSortOrder(pColumnForInitialSorting); } /** * Checks sorting type applied to given Column * @param pColumn column index * @return soring type */ public SortOrder getSortingStatus(final int pColumn) { checkColumn(pColumn); final List sortKeys = new ArrayList<>(getSortKeys()); for(SortKey sortKey : sortKeys) { if(sortKey.getColumn() == pColumn) { return sortKey.getSortOrder(); } } return SortOrder.UNSORTED; } /** * Removes all applied sorting. */ public void clearSortingState() { setSortKeys(Collections.emptyList()); } /** * Checks if any sorting type (other than UNSORTED) is applied to the table * @return true if any sorting type (other than UNSORTED) was applied, false otherwise */ public boolean isSorting() { return !getSortKeys().stream().allMatch(key -> key.getSortOrder() == SortOrder.UNSORTED); } /** * Overrides sorting for given column, or add new one * @param pColumn column index * @param pSortOrder new sort order for given column */ public void setSortingStatus(final int pColumn, final SortOrder pSortOrder) { final List sortKeys = new ArrayList<>(getSortKeys()); SortKey newSortKey = new SortKey(pColumn, pSortOrder); updateSortKeys(sortKeys, newSortKey); setSortKeys(sortKeys); } /** * Updates given list of SortKeys: * - if {@param pNewSortKey} exists in given list {@param pSortKeys} by the means of * column ID, then its replaced (removed, and added at the end) * - if {@param pNewSortKey} does NOT exists in given list {@param pSortKeys} * by the means of column ID, then its added * @param pSortKeys current sorting * @param pNewSortKey new sorting of particular column */ private void updateSortKeys(List pSortKeys, SortKey pNewSortKey) { Iterator sortKeyIterator = pSortKeys.iterator(); while(sortKeyIterator.hasNext()) { final SortKey sortKey = sortKeyIterator.next(); if(sortKey.getColumn() == pNewSortKey.getColumn()) { sortKeyIterator.remove(); break; } } pSortKeys.add(pNewSortKey); } /** * We need to override {@link javax.swing.DefaultRowSorter#toggleSortOrder} * in order to 'toggle' in a different way: * ASCENDING - DESCENDING - UNSORTED * By default, no UNSORTED is considered * * @param pColumn index of the column to make the primary sorted column, * in terms of the underlying model */ @Override public void toggleSortOrder(int pColumn) { checkColumn(pColumn); if (isSortable(pColumn)) { List keys = new ArrayList<>(getSortKeys()); SortKey sortKey; int sortIndex; for (sortIndex = keys.size() - 1; sortIndex >= 0; sortIndex--) { if (keys.get(sortIndex).getColumn() == pColumn) { break; } } if (sortIndex == -1) { // Key doesn't exist sortKey = new SortKey(pColumn, 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 SortKey(pColumn, SortOrder.ASCENDING)); } if (keys.size() > getMaxSortKeys()) { keys = keys.subList(0, getMaxSortKeys()); } setSortKeys(keys); } } /** * Modified javax.swing.DefaultRowSorter#toggle(SortKey) method using also UNSORTED type * @param key sort key to toggle * @return toggled sort key ASCENDING -> DESCENDING -> UNSORTED */ private SortKey toggle(SortKey key) { if (key.getSortOrder() == SortOrder.ASCENDING) { return new SortKey(key.getColumn(), SortOrder.DESCENDING); } else if (key.getSortOrder() == SortOrder.DESCENDING) { return new SortKey(key.getColumn(), SortOrder.UNSORTED); } return new SortKey(key.getColumn(), SortOrder.ASCENDING); } /** * @return the number of (filtered) rows */ public int getRowCount() { return (getModelWrapper() == null) ? 0 : getModelWrapper().getRowCount(); } /** * @return the number of (filtered) columns */ public int getColumnCount() { return (getModelWrapper() == null) ? 0 : getModelWrapper().getColumnCount(); } /** * Get the column name. * @param pColumn the column index * @return the name of the column */ public String getColumnName(final int pColumn) { checkColumn(pColumn); return getModelWrapper().getModel().getColumnName(pColumn); } /** * Get the column class. * @param pColumn the column index * @return the class of the column */ public Class getColumnClass(final int pColumn) { checkColumn(pColumn); return getModelWrapper().getModel().getColumnClass(pColumn); } /** * @return the underlying table model */ public TableModel getTableModel() { return getModel(); } /** * Change the table model * @param pTableModel the new table model */ public void setTableModel(final M pTableModel) { setModel(pTableModel); } /** * Check if the column is valid * @param pColumn column index to check. * @throws IndexOutOfBoundsException if the column is invalid */ private void checkColumn(int pColumn) { if (getModelWrapper() == null) { throw new IllegalStateException("Model not initialised"); } if (pColumn < 0 || pColumn >= getModelWrapper().getColumnCount()) { throw new IndexOutOfBoundsException("column "+pColumn+" beyond range of TableModel "+getModelWrapper().getColumnCount()); } } /** * Adapted standard {@link TableRowSorter#getComparator} method in order to handle * comparison of arrays, as well as supporting a default Collator. *

* The default comparator is a Collator, which is meant for String class. * Because there could be a mismatch - xml table definition will say a column is a String, * but in model (ValueModel) it is something else (e.g. Long) - then brute force casting from Collator won't work. * That's why we need to get rid of Collator and use more generic comparator based on Comparable interface. * * @param pColumn the column to fetch the Comparator for, in * terms of the underlying model * @return comparator */ @Override public Comparator getComparator(int pColumn) { Comparator comparator = super.getComparator(pColumn); // if comparator was set, and it's not default Collator - return it if (comparator != null && !(comparator instanceof Collator)) { return comparator; } Class columnClass = getModel().getColumnClass(pColumn); // standard case - make use of comparable interface if (Comparable.class.isAssignableFrom(columnClass)) { return COMPARABLE_COMPARATOR; } // handle arrays otherwise return LEXICAL_COMPARATOR; } /* * Helper classes */ /** * Static Lexical-Comparator with a special handling of String[]'s. */ public static final Comparator LEXICAL_COMPARATOR = (pO1, pO2) -> { // compare the first element of each array if (pO1 instanceof String[] && pO2 instanceof String[]) { String[] s1 = (String[]) pO1; String[] s2 = (String[]) pO2; if (s1.length > 0 && s2.length > 0) { return s1[0].compareTo(s2[0]); } } String s1 = pO1.toString(); String s2 = pO2.toString(); //noinspection StringEquality if (s1 == s2) { return 0; } if (s1 == null) { return -1; } if (s2 == null) { return 1; } return s1.compareTo(s2); }; private static class ComparableComparator implements Comparator> { @Override public int compare(Comparable o1, Comparable o2) { //noinspection unchecked return o1.compareTo(o2); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy