File: AbstractTableComparatorChooser.java

package info (click to toggle)
libglazedlists-java 1.9.0%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 3,024 kB
  • ctags: 4,252
  • sloc: java: 22,561; xml: 818; sh: 51; makefile: 5
file content (322 lines) | stat: -rw-r--r-- 14,619 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
/* Glazed Lists                                                 (c) 2003-2006 */
/* http://publicobject.com/glazedlists/                      publicobject.com,*/
/*                                                     O'Dell Engineering Ltd.*/
package ca.odell.glazedlists.gui;

import ca.odell.glazedlists.SortedList;
import ca.odell.glazedlists.impl.gui.MouseKeyboardSortingStrategy;
import ca.odell.glazedlists.impl.gui.MouseOnlySortingStrategy;
import ca.odell.glazedlists.impl.gui.SortingState;
import ca.odell.glazedlists.impl.gui.MouseOnlySortingStrategyWithUndo;
import ca.odell.glazedlists.impl.sort.TableColumnComparator;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Comparator;
import java.util.List;

/**
 * A TableComparatorChooser is a tool that allows the user to sort a table
 * widget by clicking on the table's headers. It requires that the table has a
 * SortedList as a source as the sorting on that list is used.
 *
 * @author <a href="mailto:kevin@swank.ca">Kevin Maltby</a>
 */
public abstract class AbstractTableComparatorChooser<E> {

    /**
     * Emulate the sorting behaviour of Windows Explorer and Mac OS X Finder.
     *
     * <p>Single clicks toggles between forward and reverse. If multiple comparators
     * are available for a particular column, they will be cycled in order.
     *
     * <p>At most one column can be sorted at a time.
     */
    public static final Object SINGLE_COLUMN = new MouseOnlySortingStrategy(false);

    /**
     * Sort multiple columns without use of the keyboard.  Single clicks cycle
     * through comparators, double clicks clear all secondary sorts before
     * performing the normal behaviour.
     *
     * <p>This is the original sorting strategy provided by Glazed Lists, with a
     * limitation that it is impossible to clear a sort order that is already in
     * place. It's designed to be used with multiple columns and multiple comparators
     * per column.
     *
     * <p>The overall behaviour is as follows:
     *
     * <li>Click: sort this column. If it's already sorted, reverse the sort order.
     * If its already reversed, sort using the column's next comparator in forward
     * order. If there are no more comparators, go to the first comparator. If there
     * are multiple sort columns, sort this column after those columns.
     *
     * <li>Double click: like a single click, but clear all sorting columns first.
     */
    public static final Object MULTIPLE_COLUMN_MOUSE = new MouseOnlySortingStrategy(true);

    /**
     * Sort multiple columns without use of the keyboard.  Single clicks cycle
     * through comparators. Single click on the last comparator of the primary
     * sort column will clear the entire sort (for all columns).
     *
     * <p>This is an improvement over the original sorting strategy provided by
     * Glazed Lists, since it gives a reasonable mechanism for clearing a sort
     * order that is already in place. It's designed to be used with multiple
     * columns and multiple comparators per column.
     *
     * <p>The overall behaviour is as follows:
     *
     * <li>Click: sort this column. If it's already sorted, reverse the sort order.
     * If its already reversed, sort using the column's next comparator in forward
     * order. If there are no more comparators, clear ALL column comparators.
     * If there are multiple sort columns, sort this column after those columns.
     */
    public static final Object MULTIPLE_COLUMN_MOUSE_WITH_UNDO = new MouseOnlySortingStrategyWithUndo();

    /**
     * Emulate the sorting behaviour of SUN's TableSorter, by Philip Milne et. al.
     *
     * <p>This is not a direct adaptation since we choose to support potentially
     * many Comparators per column, whereas TableSorter is limited to one.
     *
     * <p>For reference, this is TableSorter's behaviour, copied shamelessly
     * from that project's source file:
     *
     * <li>Mouse-click: Clears the sorting gui of all other columns and advances
     * the sorting gui of that column through three values:
     * {NOT_SORTED, ASCENDING, DESCENDING} (then back to NOT_SORTED again).
     *
     * <li>SHIFT-mouse-click: Clears the sorting gui of all other columns and
     * cycles the sorting gui of the column through the same three values,
     * in the opposite order: {NOT_SORTED, DESCENDING, ASCENDING}.
     *
     * <li>CONTROL-mouse-click and CONTROL-SHIFT-mouse-click: as above except that the
     * changes to the column do not cancel the statuses of columns that are
     * already sorting - giving a way to initiate a compound sort.
     *
     * @see <a href="http://java.sun.com/docs/books/tutorial/uiswing/components/table.html">Table tutorial</a>
     */
    public static final Object MULTIPLE_COLUMN_KEYBOARD = new MouseKeyboardSortingStrategy();

    /** the sorted list to choose the comparators for */
    protected SortedList<E> sortedList;

    /** the columns to sort over */
    private TableFormat<? super E> tableFormat;

    /** the potentially foreign comparator associated with the sorted list */
    protected Comparator<? super E> sortedListComparator = null;

    /** manage which columns are sorted and in which order */
    protected SortingState sortingState;

    /**
     * Create a {@link AbstractTableComparatorChooser} that sorts the specified
     * {@link SortedList} over the specified columns.
     */
    protected AbstractTableComparatorChooser(SortedList<E> sortedList, TableFormat<? super E> tableFormat) {
        this.sortedList = sortedList;
        this.sortingState = createSortingState();
        this.setTableFormat(tableFormat);

        this.sortingState.addPropertyChangeListener(new SortingStateListener());
    }

    /**
     * Returns the object which models the current sorting state of all columns
     * in the table.
     */
    protected SortingState createSortingState() {
        return new SortingState(this);
    }

    /**
     * Handle changes to the sorting state by applying the new comparator
     * to the {@link SortedList}.
     */
    private class SortingStateListener implements PropertyChangeListener {
        public void propertyChange(PropertyChangeEvent propertyChangeEvent) {
            rebuildComparator();
        }
    }

    /**
     * Updates the comparator in use and applies it to the table.
     */
    protected void rebuildComparator() {
        final Comparator<E> rebuiltComparator = sortingState.buildComparator();

        // select the new comparator
        sortedList.getReadWriteLock().writeLock().lock();
        try {
            sortedListComparator = rebuiltComparator;
            sortedList.setComparator(rebuiltComparator);
        } finally {
            sortedList.getReadWriteLock().writeLock().unlock();
        }
    }

    /**
     * Adjusts the TableFormat this comparator chooser uses when selecting
     * comparators. Calling this method will clear any active sorting.
     */
    protected void setTableFormat(TableFormat<? super E> tableFormat) {
        this.tableFormat = tableFormat;

        // handle a change in the layout of our columns
        sortingState.rebuildColumns(tableFormat);
    }


    /**
     * Gets the list of comparators for the specified column. The user is
     * free to add comparators to this list or clear the list if the specified
     * column cannot be sorted.
     */
    public List<Comparator> getComparatorsForColumn(int column) {
        return sortingState.getColumns().get(column).getComparators();
    }

    /**
     * Get the columns that the TableComparatorChooser is sorting by.
     *
     * @return a List of Integers. The first Integer is the primary sorting column,
     *      the second is the secondary, etc. This list may be empty but never null.
     */
    public List<Integer> getSortingColumns() {
        return sortingState.getSortingColumnIndexes();
    }

    /**
     * Gets the index comparator in use for the specified column. This comparator
     * may be retrieved using {@link #getComparatorsForColumn(int)}.
     *
     * @return the comparator index for the specified column, or -1 if that column
     *      is not being used to sort.
     */
    public int getColumnComparatorIndex(int column) {
        return sortingState.getColumns().get(column).getComparatorIndex();
    }

    /**
     * Gets whether the comparator in use for the specified column is reverse.
     */
    public boolean isColumnReverse(int column) {
        return sortingState.getColumns().get(column).isReverse();
    }

    /**
     * Append the comparator specified by the column, comparator index and reverse
     * parameters to the end of the sequence of comparators this
     * {@link AbstractTableComparatorChooser} is sorting the {@link SortedList}
     * by.
     *
     * <p><i>Append</i> implies that if this {@link AbstractTableComparatorChooser}
     * is already sorting that list by another column, this comparator will only
     * be used to break ties from that {@link Comparator}. If the table is already
     * sorting by the specified column, it will be silently discarded.
     *
     * <p>Suppose we're currently not sorting the table, this method will cause
     * the table to be sorted by the column specified. If we are sorting the table
     * by some column c, this will sort by that column first and the column
     * specified here second.
     *
     * <p>If this {@link AbstractTableComparatorChooser} doesn't support multiple
     * column sort, this will replace the current {@link Comparator} rather than
     * appending to it.
     *
     * @param column the column to sort by
     * @param comparatorIndex the comparator to use, specify <code>0</code> for the
     *      default comparator.
     * @param reverse whether to reverse the specified comparator.
     */
    public void appendComparator(int column, int comparatorIndex, boolean reverse) {
        sortingState.appendComparator(column, comparatorIndex, reverse);
        sortingState.fireSortingChanged();
    }

    /**
     * Clear all sorting state and set the {@link SortedList} to use its
     * natural order.
     */
    public void clearComparator() {
        sortingState.clearComparators();
        sortingState.fireSortingChanged();
    }

    /**
     * Examines the current {@link Comparator} of the SortedList and
     * adds icons to the table header renderers in response.
     *
     * <p>To do this, clicks are injected into each of the
     * corresponding <code>ColumnClickTracker</code>s.
     */
    protected void redetectComparator(Comparator<? super E> currentComparator) {
        sortedListComparator = currentComparator;
        sortingState.detectStateFromComparator(currentComparator);
    }

    /**
     * Gets the sorting style currently applied to the specified column.
     */
    protected int getSortingStyle(int column) {
        return sortingState.getColumns().get(column).getSortingStyle();
    }

    /**
     * Creates a {@link Comparator} that can compare list elements
     * given a {@link Comparator} that can compare column values for the specified
     * column. This returns a {@link Comparator} that extracts the table values for
     * the specified column and then delegates the actual comparison to the specified
     * comparator.
     */
    public Comparator createComparatorForElement(Comparator<E> comparatorForColumn, int column) {
        return new TableColumnComparator<E>(tableFormat, column, comparatorForColumn);
    }

    /**
     * Encode the current sorting state as a {@link String}. This specially formatted
     * {@link String} is ideal for persistence using any preferences API. The
     * state of this {@link AbstractTableComparatorChooser} can be restored
     * by passing the return value of this method to {@link #fromString(String)}.
     */
    @Override
    public String toString() {
        return sortingState.toString();
    }

    /**
     * <p>This class is capable of representing its own state with a String, to
     * persist sorting state externally. The format uses zero or more column specifications,
     * separated by commas. Here are some valid examples:
     *
     * <table border><tr><th>String Representation</th><th>Description</th></tr>
     * <tr><td><code>"column 3"</code></td><td>Sort using the column at index 3, using that column's first comparator, in forward order</td></tr>
     * <tr><td><code>"column 3 reversed"</code></td><td>Sort using the column at index 3, using that column's first comparator, in reverse order</td></tr>
     * <tr><td><code>"column 3, column 1"</code></td><td>Sort using the column at index 3, using that column's first comparator, in forward order<br>
     *                                     <i>then by</i><br> the column at index 1, using that column's first comparator, in forward order.</td></tr>
     * <tr><td><code>"column 3 comparator 2"</code></td><td>Sort using the column at index 3, using that column's comparator at index 2, in forward order</td></tr>
     * <tr><td><code>"column 3 comparator 2 reversed"</code></td><td>Sort using the column at index 3, using that column's comparator at index 2, in reverse order</td></tr>
     * <tr><td><code>"column 3 reversed, column 1 comparator 2, column 5 comparator 1 reversed, column 0"</code></td><td>Sort using the column at index 3, using that column's first comparator, in reverse order<br>
     *                                     <i>then by</i><br> the column at index 1, using that column's comparator at index 2, in forward order<br>
     *                                     <i>then by</i><br> the column at index 5, using that column's comparator at index 1, in reverse order<br>
     *                                     <i>then by</i><br> the column at index 0, using that column's first comparator, in forward order.</td></tr>
     * </table>
     *
     * <p>More formally, the grammar for this String representation is as follows:
     * <br><code>&lt;COLUMN&gt; = column &lt;COLUMN INDEX&gt; (comparator &lt;COMPARATOR INDEX&gt;)? (reversed)?</code>
     * <br><code>&lt;COMPARATOR SPEC&gt; = ( &lt;COLUMN&gt; (, &lt;COLUMN&gt;)* )?</code>
     */
    public void fromString(String stringEncoded) {
        sortingState.fromString(stringEncoded);
        sortingState.fireSortingChanged();
    }

    public void dispose() {
        // null out references to potentially long lived objects
        this.sortedList = null;
        this.tableFormat = null;
        this.sortedListComparator = null;
    }
}