jdk

Форк
0
/
TableSorter.java 
351 строка · 12.4 Кб
1
/*
2
 * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
3
 *
4
 * Redistribution and use in source and binary forms, with or without
5
 * modification, are permitted provided that the following conditions
6
 * are met:
7
 *
8
 *   - Redistributions of source code must retain the above copyright
9
 *     notice, this list of conditions and the following disclaimer.
10
 *
11
 *   - Redistributions in binary form must reproduce the above copyright
12
 *     notice, this list of conditions and the following disclaimer in the
13
 *     documentation and/or other materials provided with the distribution.
14
 *
15
 *   - Neither the name of Oracle nor the names of its
16
 *     contributors may be used to endorse or promote products derived
17
 *     from this software without specific prior written permission.
18
 *
19
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
20
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
23
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
 */
31

32
/*
33
 * This source code is provided to illustrate the usage of a given feature
34
 * or technique and has been deliberately simplified. Additional steps
35
 * required for a production-quality application, such as security checks,
36
 * input validation and proper error handling, might not be present in
37
 * this sample code.
38
 */
39

40

41

42
import javax.swing.table.TableModel;
43
import javax.swing.event.TableModelEvent;
44
import java.awt.event.MouseAdapter;
45
import java.awt.event.MouseEvent;
46
import java.awt.event.InputEvent;
47
import java.util.ArrayList;
48
import java.util.Date;
49
import java.util.List;
50
import javax.swing.JTable;
51
import javax.swing.table.JTableHeader;
52
import javax.swing.table.TableColumnModel;
53

54

55
/**
56
 * A sorter for TableModels. The sorter has a model (conforming to TableModel)
57
 * and itself implements TableModel. TableSorter does not store or copy
58
 * the data in the TableModel, instead it maintains an array of
59
 * integers which it keeps the same size as the number of rows in its
60
 * model. When the model changes it notifies the sorter that something
61
 * has changed eg. "rowsAdded" so that its internal array of integers
62
 * can be reallocated. As requests are made of the sorter (like
63
 * getValueAt(row, col) it redirects them to its model via the mapping
64
 * array. That way the TableSorter appears to hold another copy of the table
65
 * with the rows in a different order. The sorting algorthm used is stable
66
 * which means that it does not move around rows when its comparison
67
 * function returns 0 to denote that they are equivalent.
68
 *
69
 * @author Philip Milne
70
 */
71
@SuppressWarnings("serial")
72
public final class TableSorter extends TableMap {
73

74
    int[] indexes;
75
    List<Integer> sortingColumns = new ArrayList<Integer>();
76
    boolean ascending = true;
77
    int compares;
78

79
    public TableSorter() {
80
        indexes = new int[0]; // For consistency.
81
    }
82

83
    public TableSorter(TableModel model) {
84
        setModel(model);
85
    }
86

87
    @Override
88
    public void setModel(TableModel model) {
89
        super.setModel(model);
90
        reallocateIndexes();
91
    }
92

93
    public int compareRowsByColumn(int row1, int row2, int column) {
94
        Class<?> type = model.getColumnClass(column);
95
        TableModel data = model;
96

97
        // Check for nulls
98

99
        Object o1 = data.getValueAt(row1, column);
100
        Object o2 = data.getValueAt(row2, column);
101

102
        // If both values are null return 0
103
        if (o1 == null && o2 == null) {
104
            return 0;
105
        } else if (o1 == null) { // Define null less than everything.
106
            return -1;
107
        } else if (o2 == null) {
108
            return 1;
109
        }
110

111
        /* We copy all returned values from the getValue call in case
112
        an optimised model is reusing one object to return many values.
113
        The Number subclasses in the JDK are immutable and so will not be used
114
        in this way but other subclasses of Number might want to do this to save
115
        space and avoid unnecessary heap allocation.
116
         */
117
        if (type.getSuperclass() == java.lang.Number.class) {
118
            Number n1 = (Number) data.getValueAt(row1, column);
119
            double d1 = n1.doubleValue();
120
            Number n2 = (Number) data.getValueAt(row2, column);
121
            double d2 = n2.doubleValue();
122

123
            if (d1 < d2) {
124
                return -1;
125
            } else if (d1 > d2) {
126
                return 1;
127
            } else {
128
                return 0;
129
            }
130
        } else if (type == java.util.Date.class) {
131
            Date d1 = (Date) data.getValueAt(row1, column);
132
            long n1 = d1.getTime();
133
            Date d2 = (Date) data.getValueAt(row2, column);
134
            long n2 = d2.getTime();
135

136
            if (n1 < n2) {
137
                return -1;
138
            } else if (n1 > n2) {
139
                return 1;
140
            } else {
141
                return 0;
142
            }
143
        } else if (type == String.class) {
144
            String s1 = (String) data.getValueAt(row1, column);
145
            String s2 = (String) data.getValueAt(row2, column);
146
            int result = s1.compareTo(s2);
147

148
            if (result < 0) {
149
                return -1;
150
            } else if (result > 0) {
151
                return 1;
152
            } else {
153
                return 0;
154
            }
155
        } else if (type == Boolean.class) {
156
            Boolean bool1 = (Boolean) data.getValueAt(row1, column);
157
            boolean b1 = bool1.booleanValue();
158
            Boolean bool2 = (Boolean) data.getValueAt(row2, column);
159
            boolean b2 = bool2.booleanValue();
160

161
            if (b1 == b2) {
162
                return 0;
163
            } else if (b1) // Define false < true
164
            {
165
                return 1;
166
            } else {
167
                return -1;
168
            }
169
        } else {
170
            Object v1 = data.getValueAt(row1, column);
171
            String s1 = v1.toString();
172
            Object v2 = data.getValueAt(row2, column);
173
            String s2 = v2.toString();
174
            int result = s1.compareTo(s2);
175

176
            if (result < 0) {
177
                return -1;
178
            } else if (result > 0) {
179
                return 1;
180
            } else {
181
                return 0;
182
            }
183
        }
184
    }
185

186
    public int compare(int row1, int row2) {
187
        compares++;
188
        for (int level = 0; level < sortingColumns.size(); level++) {
189
            Integer column = sortingColumns.get(level);
190
            int result = compareRowsByColumn(row1, row2, column.intValue());
191
            if (result != 0) {
192
                return ascending ? result : -result;
193
            }
194
        }
195
        return 0;
196
    }
197

198
    public void reallocateIndexes() {
199
        int rowCount = model.getRowCount();
200

201
        // Set up a new array of indexes with the right number of elements
202
        // for the new data model.
203
        indexes = new int[rowCount];
204

205
        // Initialise with the identity mapping.
206
        for (int row = 0; row < rowCount; row++) {
207
            indexes[row] = row;
208
        }
209
    }
210

211
    @Override
212
    public void tableChanged(TableModelEvent e) {
213
        System.out.println("Sorter: tableChanged");
214
        reallocateIndexes();
215

216
        super.tableChanged(e);
217
    }
218

219
    public void checkModel() {
220
        if (indexes.length != model.getRowCount()) {
221
            System.err.println("Sorter not informed of a change in model.");
222
        }
223
    }
224

225
    public void sort(Object sender) {
226
        checkModel();
227

228
        compares = 0;
229
        // n2sort();
230
        // qsort(0, indexes.length-1);
231
        shuttlesort(indexes.clone(), indexes, 0, indexes.length);
232
        System.out.println("Compares: " + compares);
233
    }
234

235
    public void n2sort() {
236
        for (int i = 0; i < getRowCount(); i++) {
237
            for (int j = i + 1; j < getRowCount(); j++) {
238
                if (compare(indexes[i], indexes[j]) == -1) {
239
                    swap(i, j);
240
                }
241
            }
242
        }
243
    }
244

245
    // This is a home-grown implementation which we have not had time
246
    // to research - it may perform poorly in some circumstances. It
247
    // requires twice the space of an in-place algorithm and makes
248
    // NlogN assigments shuttling the values between the two
249
    // arrays. The number of compares appears to vary between N-1 and
250
    // NlogN depending on the initial order but the main reason for
251
    // using it here is that, unlike qsort, it is stable.
252
    public void shuttlesort(int[] from, int[] to, int low, int high) {
253
        if (high - low < 2) {
254
            return;
255
        }
256
        int middle = (low + high) / 2;
257
        shuttlesort(to, from, low, middle);
258
        shuttlesort(to, from, middle, high);
259

260
        int p = low;
261
        int q = middle;
262

263
        /* This is an optional short-cut; at each recursive call,
264
        check to see if the elements in this subset are already
265
        ordered.  If so, no further comparisons are needed; the
266
        sub-array can just be copied.  The array must be copied rather
267
        than assigned otherwise sister calls in the recursion might
268
        get out of sinc.  When the number of elements is three they
269
        are partitioned so that the first set, [low, mid), has one
270
        element and the second, [mid, high), has two. We skip the
271
        optimisation when the number of elements is three or less as
272
        the first compare in the normal merge will produce the same
273
        sequence of steps. This optimisation seems to be worthwhile
274
        for partially ordered lists but some analysis is needed to
275
        find out how the performance drops to Nlog(N) as the initial
276
        order diminishes - it may drop very quickly.  */
277

278
        if (high - low >= 4 && compare(from[middle - 1], from[middle]) <= 0) {
279
            System.arraycopy(from, low, to, low, high - low);
280
            return;
281
        }
282

283
        // A normal merge.
284

285
        for (int i = low; i < high; i++) {
286
            if (q >= high || (p < middle && compare(from[p], from[q]) <= 0)) {
287
                to[i] = from[p++];
288
            } else {
289
                to[i] = from[q++];
290
            }
291
        }
292
    }
293

294
    public void swap(int i, int j) {
295
        int tmp = indexes[i];
296
        indexes[i] = indexes[j];
297
        indexes[j] = tmp;
298
    }
299

300
    // The mapping only affects the contents of the data rows.
301
    // Pass all requests to these rows through the mapping array: "indexes".
302
    @Override
303
    public Object getValueAt(int aRow, int aColumn) {
304
        checkModel();
305
        return model.getValueAt(indexes[aRow], aColumn);
306
    }
307

308
    @Override
309
    public void setValueAt(Object aValue, int aRow, int aColumn) {
310
        checkModel();
311
        model.setValueAt(aValue, indexes[aRow], aColumn);
312
    }
313

314
    public void sortByColumn(int column) {
315
        sortByColumn(column, true);
316
    }
317

318
    public void sortByColumn(int column, boolean ascending) {
319
        this.ascending = ascending;
320
        sortingColumns.clear();
321
        sortingColumns.add(column);
322
        sort(this);
323
        super.tableChanged(new TableModelEvent(this));
324
    }
325

326
    // There is no-where else to put this.
327
    // Add a mouse listener to the Table to trigger a table sort
328
    // when a column heading is clicked in the JTable.
329
    public void addMouseListenerToHeaderInTable(JTable table) {
330
        final TableSorter sorter = this;
331
        final JTable tableView = table;
332
        tableView.setColumnSelectionAllowed(false);
333
        MouseAdapter listMouseListener = new MouseAdapter() {
334

335
            @Override
336
            public void mouseClicked(MouseEvent e) {
337
                TableColumnModel columnModel = tableView.getColumnModel();
338
                int viewColumn = columnModel.getColumnIndexAtX(e.getX());
339
                int column = tableView.convertColumnIndexToModel(viewColumn);
340
                if (e.getClickCount() == 1 && column != -1) {
341
                    System.out.println("Sorting ...");
342
                    int shiftPressed = e.getModifiersEx() & InputEvent.SHIFT_DOWN_MASK;
343
                    boolean ascending = (shiftPressed == 0);
344
                    sorter.sortByColumn(column, ascending);
345
                }
346
            }
347
        };
348
        JTableHeader th = tableView.getTableHeader();
349
        th.addMouseListener(listMouseListener);
350
    }
351
}
352

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.