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

ca.odell.glazedlists.impl.gui.SortingState Maven / Gradle / Ivy

/* Glazed Lists                                                 (c) 2003-2006 */
/* http://publicobject.com/glazedlists/                      publicobject.com,*/
/*                                                     O'Dell Engineering Ltd.*/
package ca.odell.glazedlists.impl.gui;

import ca.odell.glazedlists.GlazedLists;
import ca.odell.glazedlists.gui.AbstractTableComparatorChooser;
import ca.odell.glazedlists.gui.AdvancedTableFormat;
import ca.odell.glazedlists.gui.TableFormat;
import ca.odell.glazedlists.impl.sort.ComparatorChain;
import ca.odell.glazedlists.impl.sort.ReverseComparator;
import ca.odell.glazedlists.impl.sort.TableColumnComparator;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Keep track of which columns are sorted and how. This is
 * largely independent of how that state is applied to a
 * SortedList, which is managed independently by
 * TableComparatorChooser.
 *
 * 

Users must explicity call {@link #fireSortingChanged()} in order * to prepare a new Comparator for the target table. * * @author Jesse Wilson */ public class SortingState { /** this regular expression for parsing the string representation of a column */ private static final Pattern FROM_STRING_PATTERN = Pattern.compile("^\\s*column\\s+(\\d+)(\\s+comparator\\s+(\\d+))?(\\s+(reversed))?\\s*$", Pattern.CASE_INSENSITIVE); /** the sorting style on a column is used for icon choosing */ protected static final int COLUMN_UNSORTED = 0; protected static final int COLUMN_PRIMARY_SORTED = 1; protected static final int COLUMN_PRIMARY_SORTED_REVERSE = 2; protected static final int COLUMN_PRIMARY_SORTED_ALTERNATE = 3; protected static final int COLUMN_PRIMARY_SORTED_ALTERNATE_REVERSE = 4; protected static final int COLUMN_SECONDARY_SORTED = 5; protected static final int COLUMN_SECONDARY_SORTED_REVERSE = 6; protected static final int COLUMN_SECONDARY_SORTED_ALTERNATE = 7; protected static final int COLUMN_SECONDARY_SORTED_ALTERNATE_REVERSE = 8; /** the columns and their click counts in indexed order */ protected List sortingColumns; /** a list that contains all ColumnClickTrackers with non-zero click counts in their visitation order */ protected List recentlyClickedColumns = new ArrayList(2); private final AbstractTableComparatorChooser tableComparatorChooser; /** whom to notify when the sorting state is chaged */ private final PropertyChangeSupport changeSupport = new PropertyChangeSupport(this); public SortingState(AbstractTableComparatorChooser tableComparatorChooser) { this.tableComparatorChooser = tableComparatorChooser; } public AbstractTableComparatorChooser getTableComparatorChooser() { return tableComparatorChooser; } public void fireSortingChanged() { changeSupport.firePropertyChange("comparator", null, null); } public void addPropertyChangeListener(PropertyChangeListener listener) { changeSupport.addPropertyChangeListener(listener); } public void removePropertyChangeListener(PropertyChangeListener listener) { changeSupport.removePropertyChangeListener(listener); } public Comparator buildComparator() { // build a new comparator if(recentlyClickedColumns.isEmpty()) { return null; } else { List> comparators = new ArrayList>(recentlyClickedColumns.size()); for(Iterator i = recentlyClickedColumns.iterator(); i.hasNext(); ) { SortingColumn sortingColumn = i.next(); Comparator comparator = sortingColumn.getComparator(); if(comparator == null) throw new IllegalStateException(); comparators.add(comparator); } return GlazedLists.chainComparators(comparators); } } /** * @return the indices of the columns currently being sorted. */ public List getSortingColumnIndexes() { final List sortingColumns = new ArrayList(); final List recentlyClickedColumns = getRecentlyClickedColumns(); for(int c = 0; c < recentlyClickedColumns.size(); c++) { SortingState.SortingColumn clickedColumn = recentlyClickedColumns.get(c); sortingColumns.add(new Integer(clickedColumn.getColumn())); } return sortingColumns; } public void appendComparator(int column, int comparatorIndex, boolean reverse) { if(column > getColumns().size()) throw new IllegalArgumentException("invalid column " + column + ", must be in range 0, " + sortingColumns.size()); if(comparatorIndex >= sortingColumns.get(column).getComparators().size()) throw new IllegalArgumentException("invalid comparator index " + comparatorIndex + ", must be in range 0, " + sortingColumns.get(column).getComparators().size()); if(recentlyClickedColumns.contains(getColumns().get(column))) return; // add clicks to the specified column SortingColumn sortingColumn = sortingColumns.get(column); sortingColumn.setComparatorIndex(comparatorIndex); sortingColumn.setReverse(reverse); // rebuild the clicked column list recentlyClickedColumns.add(sortingColumn); } public void detectStateFromComparator(Comparator foreignComparator) { // Clear the current click counts clearComparators(); // Populate a list of Comparators final List comparatorsList; if(foreignComparator == null) { comparatorsList = Collections.emptyList(); } else if(foreignComparator instanceof ComparatorChain) { ComparatorChain chain = (ComparatorChain)foreignComparator; comparatorsList = Arrays.asList(chain.getComparators()); } else { comparatorsList = Collections.singletonList(foreignComparator); } // walk through the list of Comparators and assign click counts for(Iterator i = comparatorsList.iterator(); i.hasNext(); ) { // get the current comparator Comparator comparator = i.next(); boolean reverse = false; if(comparator instanceof ReverseComparator) { reverse = true; comparator = ((ReverseComparator)comparator).getSourceComparator(); } // discover where to add clicks for this comparator for(int c = 0; c < sortingColumns.size(); c++) { if(recentlyClickedColumns.contains(sortingColumns.get(c))) { continue; } int comparatorIndex = sortingColumns.get(c).getComparators().indexOf(comparator); if(comparatorIndex != -1) { final SortingColumn columnClickTracker = sortingColumns.get(c); columnClickTracker.setComparatorIndex(comparatorIndex); columnClickTracker.setReverse(reverse); recentlyClickedColumns.add(columnClickTracker); } } } } public void clearComparators() { // clear the click counts for(Iterator i = recentlyClickedColumns.iterator(); i.hasNext(); ) { SortingColumn sortingColumn = i.next(); sortingColumn.clear(); } recentlyClickedColumns.clear(); } /** * When the column model is changed, this resets the column clicks and * comparator list for each column. */ public void rebuildColumns(TableFormat tableFormat) { // build the column click trackers final int columnCount = tableFormat.getColumnCount(); sortingColumns = new ArrayList(columnCount); for(int i = 0; i < columnCount; i++) { sortingColumns.add(createSortingColumn(tableFormat, i)); } recentlyClickedColumns.clear(); } protected SortingColumn createSortingColumn(TableFormat tableFormat, int columnIndex) { return new SortingColumn(tableFormat, columnIndex); } public List getColumns() { return sortingColumns; } public List getRecentlyClickedColumns() { return recentlyClickedColumns; } @Override public String toString() { final StringBuffer result = new StringBuffer(); for(Iterator i = getSortingColumnIndexes().iterator(); i.hasNext();) { final int columnIndex = i.next().intValue(); final SortingState.SortingColumn sortingColumn = getColumns().get(columnIndex); // write the column index result.append("column "); result.append(columnIndex); // write the comparator index final int comparatorIndex = sortingColumn.getComparatorIndex(); if(comparatorIndex != 0) { result.append(" comparator "); result.append(comparatorIndex); } // write reversed if(sortingColumn.isReverse()) { result.append(" reversed"); } // add a comma if more columns exist if (i.hasNext()) { result.append(", "); } } return result.toString(); } public void fromString(String stringEncoded) { clearComparators(); // parse each column part in sequence using regex groups String[] parts = stringEncoded.split(","); for(int p = 0; p < parts.length; p++) { // skip empty strings if(parts[p].trim().length() == 0) continue; Matcher matcher = FROM_STRING_PATTERN.matcher(parts[p]); if(!matcher.find()) throw new IllegalArgumentException("Failed to parse column spec, \"" + parts[p] + "\""); int columnIndex = Integer.parseInt(matcher.group(1)); int comparatorIndex = matcher.group(3) == null ? 0 : Integer.parseInt(matcher.group(3)); boolean reversedComparator = matcher.group(5) != null; // bail on invalid data if(columnIndex >= sortingColumns.size()) continue; if(comparatorIndex >= sortingColumns.get(columnIndex).getComparators().size()) continue; // add this comparator in sequence appendComparator(columnIndex, comparatorIndex, reversedComparator); } } public class SortingColumn { /** the column whose sorting state is being managed */ private final int column; /** the sequence of comparators for this column */ private final List comparators = new ArrayList(1); /** whether this column is sorted in reverse order */ private boolean reverse = false; /** the comparator in the comparator list to sort by */ private int comparatorIndex = -1; public SortingColumn(TableFormat tableFormat, int column) { this.column = column; // add the preferred comparator for AdvancedTableFormat if(tableFormat instanceof AdvancedTableFormat) { AdvancedTableFormat advancedTableFormat = (AdvancedTableFormat)tableFormat; Comparator columnComparator = advancedTableFormat.getColumnComparator(column); if(columnComparator != null) comparators.add(new TableColumnComparator(tableFormat, column, columnComparator)); // otherwise just add the default comparator } else { comparators.add(new TableColumnComparator(tableFormat, column)); } } public void clear() { this.reverse = false; this.comparatorIndex = -1; } public int getColumn() { return column; } /** * Gets the index of the comparator to use for this column. */ public void setComparatorIndex(int comparatorIndex) { assert(comparatorIndex < comparators.size()); this.comparatorIndex = comparatorIndex; } public int getComparatorIndex() { return comparatorIndex; } /** * Gets the list of comparators for this column. */ public List getComparators() { return comparators; } /** * Gets the current best comparator to sort this column. */ public Comparator getComparator() { if(comparatorIndex == -1) return null; Comparator comparator = comparators.get(getComparatorIndex()); if(isReverse()) comparator = GlazedLists.reverseComparator(comparator); return comparator; } /** * Get whether this column is in reverse order. */ public boolean isReverse() { return reverse; } public void setReverse(boolean reverse) { this.reverse = reverse; } /** * Gets the sorting style for this column. */ public int getSortingStyle() { if(comparatorIndex == -1) return COLUMN_UNSORTED; boolean primaryColumn = !recentlyClickedColumns.isEmpty() && recentlyClickedColumns.get(0) == this; boolean primaryComparator = getComparatorIndex() == 0; if(primaryColumn) { if(!isReverse()) { if(primaryComparator) return COLUMN_PRIMARY_SORTED; else return COLUMN_PRIMARY_SORTED_ALTERNATE; } else { if(primaryComparator) return COLUMN_PRIMARY_SORTED_REVERSE; else return COLUMN_PRIMARY_SORTED_ALTERNATE_REVERSE; } } else { if(!isReverse()) { if(primaryComparator) return COLUMN_SECONDARY_SORTED; else return COLUMN_SECONDARY_SORTED_ALTERNATE; } else { if(primaryComparator) return COLUMN_SECONDARY_SORTED_REVERSE; else return COLUMN_SECONDARY_SORTED_ALTERNATE_REVERSE; } } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy