001: /*
002: * Modifications of Sun Microsystems code:
003: * <copyright>
004: *
005: * Copyright 1997-2004 BBNT Solutions, LLC
006: * under sponsorship of the Defense Advanced Research Projects
007: * Agency (DARPA).
008: *
009: * You can redistribute this software and/or modify it under the
010: * terms of the Cougaar Open Source License as published on the
011: * Cougaar Open Source Website (www.cougaar.org).
012: *
013: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
014: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
015: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
016: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
017: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
018: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
019: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
020: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
021: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
022: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
023: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
024: *
025: * </copyright>
026: */
027:
028: package org.cougaar.util;
029:
030: /*
031: * @(#)TableSorter.java 1.8 99/04/23
032: *
033: * Copyright (c) 1997-1999 by Sun Microsystems, Inc. All Rights Reserved.
034: *
035: * Sun grants you ("Licensee") a non-exclusive, royalty free, license to use,
036: * modify and redistribute this software in source and binary code form,
037: * provided that i) this copyright notice and license appear on all copies of
038: * the software; and ii) Licensee does not utilize the software in a manner
039: * which is disparaging to Sun.
040: *
041: * This software is provided "AS IS," without a warranty of any kind. ALL
042: * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
043: * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
044: * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
045: * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
046: * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
047: * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
048: * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
049: * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
050: * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
051: * POSSIBILITY OF SUCH DAMAGES.
052: *
053: * This software is not designed or intended for use in on-line control of
054: * aircraft, air traffic, aircraft navigation or aircraft communications; or in
055: * the design, construction, operation or maintenance of any nuclear
056: * facility. Licensee represents and warrants that it will not use or
057: * redistribute the Software for such purposes.
058: */
059:
060: /**
061: * A sorter for TableModels. The sorter has a model (conforming to TableModel)
062: * and itself implements TableModel. TableSorter does not store or copy
063: * the data in the TableModel, instead it maintains an array of
064: * integers which it keeps the same size as the number of rows in its
065: * model. When the model changes it notifies the sorter that something
066: * has changed eg. "rowsAdded" so that its internal array of integers
067: * can be reallocated. As requests are made of the sorter (like
068: * getValueAt(row, col) it redirects them to its model via the mapping
069: * array. That way the TableSorter appears to hold another copy of the table
070: * with the rows in a different order. The sorting algorthm used is stable
071: * which means that it does not move around rows when its comparison
072: * function returns 0 to denote that they are equivalent.
073: *
074: * @version 1.8 04/23/99
075: * @author Philip Milne
076: */
077:
078: import java.awt.event.InputEvent;
079: import java.awt.event.MouseAdapter;
080: import java.awt.event.MouseEvent;
081: import java.util.Vector;
082:
083: import javax.swing.JTable;
084: import javax.swing.event.TableModelEvent;
085: import javax.swing.table.JTableHeader;
086: import javax.swing.table.TableColumnModel;
087: import javax.swing.table.TableModel;
088:
089: public abstract class TableSorter extends TableMap {
090: int indexes[] = new int[0];
091: /**
092: * The columns to sort by. The last element is the primary
093: * sort. Earlier elements are secondary sort.
094: **/
095: Vector sortingColumns = new Vector();
096: boolean ascending = true;
097: int compares;
098:
099: public TableSorter() {
100: indexes = new int[0]; // For consistency.
101: }
102:
103: public TableSorter(TableModel model) {
104: setModel(model);
105: }
106:
107: public void setModel(TableModel model) {
108: super .setModel(model);
109: reallocateIndexes();
110: }
111:
112: public abstract int compareRowsByColumn(int row1, int row2,
113: int column);
114:
115: public int compare(int row1, int row2) {
116: compares++;
117: for (int level = sortingColumns.size(); --level >= 0;) {
118: Integer column = (Integer) sortingColumns.elementAt(level);
119: int result = compareRowsByColumn(row1, row2, column
120: .intValue());
121: if (result != 0)
122: return ascending ? result : -result;
123: }
124: return 0;
125: }
126:
127: public void reallocateIndexes() {
128: int rowCount = model.getRowCount();
129:
130: // Set up a new array of indexes with the right number of elements
131: // for the new data model.
132: indexes = new int[rowCount];
133:
134: // Initialise with the identity mapping.
135: for (int row = 0; row < rowCount; row++)
136: indexes[row] = row;
137: }
138:
139: public void tableChanged(TableModelEvent e) {
140: reallocateIndexes();
141:
142: super .tableChanged(e);
143: }
144:
145: public void checkModel() {
146: if (indexes.length != model.getRowCount()) {
147: System.err
148: .println("Sorter not informed of a change in model.");
149: }
150: }
151:
152: public void sort(Object sender) {
153: checkModel();
154:
155: compares = 0;
156: // n2sort();
157: // qsort(0, indexes.length-1);
158: shuttlesort((int[]) indexes.clone(), indexes, 0, indexes.length);
159: }
160:
161: public void n2sort() {
162: for (int i = 0; i < getRowCount(); i++) {
163: for (int j = i + 1; j < getRowCount(); j++) {
164: if (compare(indexes[i], indexes[j]) == -1) {
165: swap(i, j);
166: }
167: }
168: }
169: }
170:
171: // This is a home-grown implementation which we have not had time
172: // to research - it may perform poorly in some circumstances. It
173: // requires twice the space of an in-place algorithm and makes
174: // NlogN assigments shuttling the values between the two
175: // arrays. The number of compares appears to vary between N-1 and
176: // NlogN depending on the initial order but the main reason for
177: // using it here is that, unlike qsort, it is stable.
178: public void shuttlesort(int from[], int to[], int low, int high) {
179: if (high - low < 2) {
180: return;
181: }
182: int middle = (low + high) / 2;
183: shuttlesort(to, from, low, middle);
184: shuttlesort(to, from, middle, high);
185:
186: int p = low;
187: int q = middle;
188:
189: /* This is an optional short-cut; at each recursive call,
190: check to see if the elements in this subset are already
191: ordered. If so, no further comparisons are needed; the
192: sub-array can just be copied. The array must be copied rather
193: than assigned otherwise sister calls in the recursion might
194: get out of sinc. When the number of elements is three they
195: are partitioned so that the first set, [low, mid), has one
196: element and and the second, [mid, high), has two. We skip the
197: optimisation when the number of elements is three or less as
198: the first compare in the normal merge will produce the same
199: sequence of steps. This optimisation seems to be worthwhile
200: for partially ordered lists but some analysis is needed to
201: find out how the performance drops to Nlog(N) as the initial
202: order diminishes - it may drop very quickly. */
203:
204: if (high - low >= 4
205: && compare(from[middle - 1], from[middle]) <= 0) {
206: for (int i = low; i < high; i++) {
207: to[i] = from[i];
208: }
209: return;
210: }
211:
212: // A normal merge.
213:
214: for (int i = low; i < high; i++) {
215: if (q >= high
216: || (p < middle && compare(from[p], from[q]) <= 0)) {
217: to[i] = from[p++];
218: } else {
219: to[i] = from[q++];
220: }
221: }
222: }
223:
224: public void swap(int i, int j) {
225: int tmp = indexes[i];
226: indexes[i] = indexes[j];
227: indexes[j] = tmp;
228: }
229:
230: // The mapping only affects the contents of the data rows.
231: // Pass all requests to these rows through the mapping array: "indexes".
232:
233: public Object getValueAt(int aRow, int aColumn) {
234: checkModel();
235: return model.getValueAt(indexes[aRow], aColumn);
236: }
237:
238: public void setValueAt(Object aValue, int aRow, int aColumn) {
239: checkModel();
240: model.setValueAt(aValue, indexes[aRow], aColumn);
241: }
242:
243: public void sortByColumn(int column) {
244: boolean ascending = true;
245: if (sortingColumns.size() > 0) {
246: Integer currentColumn = (Integer) sortingColumns
247: .lastElement();
248: if (currentColumn.intValue() == column) {
249: ascending = !this .ascending;
250: }
251: }
252: sortByColumn(column, ascending);
253: }
254:
255: public void sortByColumn(int column, boolean ascending) {
256: this .ascending = ascending;
257: Integer nv = new Integer(column);
258: sortingColumns.remove(nv);
259: sortingColumns.addElement(nv);
260: sort(this );
261: super .tableChanged(new TableModelEvent(this ));
262: }
263:
264: // There is no-where else to put this.
265: // Add a mouse listener to the Table to trigger a table sort
266: // when a column heading is clicked in the JTable.
267: public void addMouseListenerToHeaderInTable(JTable table) {
268: final TableSorter sorter = this ;
269: final JTable tableView = table;
270: tableView.setColumnSelectionAllowed(false);
271: MouseAdapter listMouseListener = new MouseAdapter() {
272: public void mouseClicked(MouseEvent e) {
273: TableColumnModel columnModel = tableView
274: .getColumnModel();
275: int viewColumn = columnModel
276: .getColumnIndexAtX(e.getX());
277: int column = tableView
278: .convertColumnIndexToModel(viewColumn);
279: if (e.getClickCount() == 1 && column != -1) {
280: int shiftPressed = e.getModifiers()
281: & InputEvent.SHIFT_MASK;
282: boolean ascending = (shiftPressed == 0);
283: sorter.sortByColumn(column, ascending);
284: }
285: }
286: };
287: JTableHeader th = tableView.getTableHeader();
288: th.addMouseListener(listMouseListener);
289: }
290:
291: }
|