1 package org.hsqldb.util; 2 3 import java.util.ArrayList ; 4 import java.util.Arrays ; 5 import java.util.Comparator ; 6 import java.util.HashMap ; 7 import java.util.Iterator ; 8 import java.util.List ; 9 import java.util.Map ; 10 import java.awt.Color ; 11 import java.awt.Component ; 12 import java.awt.Graphics ; 13 import java.awt.event.MouseAdapter ; 14 import java.awt.event.MouseEvent ; 15 import java.awt.event.MouseListener ; 16 17 import javax.swing.Icon ; 18 import javax.swing.JLabel ; 19 import javax.swing.JTable ; 20 import javax.swing.event.TableModelEvent ; 21 import javax.swing.event.TableModelListener ; 22 import javax.swing.table.AbstractTableModel ; 23 import javax.swing.table.JTableHeader ; 24 import javax.swing.table.TableCellRenderer ; 25 import javax.swing.table.TableColumnModel ; 26 import javax.swing.table.TableModel ; 27 28 79 public class TableSorter extends AbstractTableModel { 80 81 protected TableModel tableModel; 82 public static final int DESCENDING = -1; 83 public static final int NOT_SORTED = 0; 84 public static final int ASCENDING = 1; 85 private static Directive EMPTY_DIRECTIVE = new Directive(-1, NOT_SORTED); 86 public static final Comparator COMPARABLE_COMPARATOR = new Comparator () { 87 88 public int compare(Object o1, Object o2) { 89 90 if (o1 == o2) { 91 return 0; 92 } 93 94 if (o1 == null) { 95 if (o2 == null) { 96 return 0; 97 } 98 99 return -1; 100 } 101 102 if (o2 == null) { 103 return 1; 104 } 105 106 return ((Comparable ) o1).compareTo(o2); 107 } 108 }; 109 public static final Comparator LEXICAL_COMPARATOR = new Comparator () { 110 111 public int compare(Object o1, Object o2) { 112 return o1.toString().compareTo(o2.toString()); 113 } 114 }; 115 private Row[] viewToModel; 116 private int[] modelToView; 117 private JTableHeader tableHeader; 118 private MouseListener mouseListener; 119 private TableModelListener tableModelListener; 120 private Map columnComparators = new HashMap (); 121 private List sortingColumns = new ArrayList (); 122 123 public TableSorter() { 124 this.mouseListener = new MouseHandler(); 125 this.tableModelListener = new TableModelHandler(); 126 } 127 128 public TableSorter(TableModel tableModel) { 129 130 this(); 131 132 setTableModel(tableModel); 133 } 134 135 public TableSorter(TableModel tableModel, JTableHeader tableHeader) { 136 137 this(); 138 139 setTableHeader(tableHeader); 140 setTableModel(tableModel); 141 } 142 143 private void clearSortingState() { 144 viewToModel = null; 145 modelToView = null; 146 } 147 148 public TableModel getTableModel() { 149 return tableModel; 150 } 151 152 public void setTableModel(TableModel tableModel) { 153 154 if (this.tableModel != null) { 155 this.tableModel.removeTableModelListener(tableModelListener); 156 } 157 158 this.tableModel = tableModel; 159 160 if (this.tableModel != null) { 161 this.tableModel.addTableModelListener(tableModelListener); 162 } 163 164 clearSortingState(); 165 fireTableStructureChanged(); 166 } 167 168 public JTableHeader getTableHeader() { 169 return tableHeader; 170 } 171 172 public void setTableHeader(JTableHeader tableHeader) { 173 174 if (this.tableHeader != null) { 175 this.tableHeader.removeMouseListener(mouseListener); 176 177 TableCellRenderer defaultRenderer = 178 this.tableHeader.getDefaultRenderer(); 179 180 if (defaultRenderer instanceof SortableHeaderRenderer) { 181 this.tableHeader.setDefaultRenderer( 182 ((SortableHeaderRenderer) defaultRenderer) 183 .tableCellRenderer); 184 } 185 } 186 187 this.tableHeader = tableHeader; 188 189 if (this.tableHeader != null) { 190 this.tableHeader.addMouseListener(mouseListener); 191 this.tableHeader.setDefaultRenderer( 192 new SortableHeaderRenderer( 193 this.tableHeader.getDefaultRenderer())); 194 } 195 } 196 197 public boolean isSorting() { 198 return sortingColumns.size() != 0; 199 } 200 201 private Directive getDirective(int column) { 202 203 for (int i = 0; i < sortingColumns.size(); i++) { 204 Directive directive = (Directive) sortingColumns.get(i); 205 206 if (directive.column == column) { 207 return directive; 208 } 209 } 210 211 return EMPTY_DIRECTIVE; 212 } 213 214 public int getSortingStatus(int column) { 215 return getDirective(column).direction; 216 } 217 218 private void sortingStatusChanged() { 219 220 clearSortingState(); 221 fireTableDataChanged(); 222 223 if (tableHeader != null) { 224 tableHeader.repaint(); 225 } 226 } 227 228 public void setSortingStatus(int column, int status) { 229 230 Directive directive = getDirective(column); 231 232 if (directive != EMPTY_DIRECTIVE) { 233 sortingColumns.remove(directive); 234 } 235 236 if (status != NOT_SORTED) { 237 sortingColumns.add(new Directive(column, status)); 238 } 239 240 sortingStatusChanged(); 241 } 242 243 protected Icon getHeaderRendererIcon(int column, int size) { 244 245 Directive directive = getDirective(column); 246 247 if (directive == EMPTY_DIRECTIVE) { 248 return null; 249 } 250 251 return new Arrow(directive.direction == DESCENDING, size, 252 sortingColumns.indexOf(directive)); 253 } 254 255 private void cancelSorting() { 256 sortingColumns.clear(); 257 sortingStatusChanged(); 258 } 259 260 public void setColumnComparator(Class type, Comparator comparator) { 261 262 if (comparator == null) { 263 columnComparators.remove(type); 264 } else { 265 columnComparators.put(type, comparator); 266 } 267 } 268 269 protected Comparator getComparator(int column) { 270 271 Class columnType = tableModel.getColumnClass(column); 272 Comparator comparator = 273 (Comparator ) columnComparators.get(columnType); 274 275 if (comparator != null) { 276 return comparator; 277 } 278 279 if (Comparable .class.isAssignableFrom(columnType)) { 280 return COMPARABLE_COMPARATOR; 281 } 282 283 return LEXICAL_COMPARATOR; 284 } 285 286 private Row[] getViewToModel() { 287 288 if (viewToModel == null) { 289 int tableModelRowCount = tableModel.getRowCount(); 290 291 viewToModel = new Row[tableModelRowCount]; 292 293 for (int row = 0; row < tableModelRowCount; row++) { 294 viewToModel[row] = new Row(row); 295 } 296 297 if (isSorting()) { 298 Arrays.sort(viewToModel); 299 } 300 } 301 302 return viewToModel; 303 } 304 305 public int modelIndex(int viewIndex) { 306 return getViewToModel()[viewIndex].modelIndex; 307 } 308 309 private int[] getModelToView() { 310 311 if (modelToView == null) { 312 int n = getViewToModel().length; 313 314 modelToView = new int[n]; 315 316 for (int i = 0; i < n; i++) { 317 modelToView[modelIndex(i)] = i; 318 } 319 } 320 321 return modelToView; 322 } 323 324 public int getRowCount() { 326 return (tableModel == null) ? 0 327 : tableModel.getRowCount(); 328 } 329 330 public int getColumnCount() { 331 return (tableModel == null) ? 0 332 : tableModel.getColumnCount(); 333 } 334 335 public String getColumnName(int column) { 336 return tableModel.getColumnName(column); 337 } 338 339 public Class getColumnClass(int column) { 340 return tableModel.getColumnClass(column); 341 } 342 343 public boolean isCellEditable(int row, int column) { 344 return tableModel.isCellEditable(modelIndex(row), column); 345 } 346 347 public Object getValueAt(int row, int column) { 348 return tableModel.getValueAt(modelIndex(row), column); 349 } 350 351 public void setValueAt(Object aValue, int row, int column) { 352 tableModel.setValueAt(aValue, modelIndex(row), column); 353 } 354 355 private class Row implements Comparable { 357 358 private int modelIndex; 359 360 public Row(int index) { 361 this.modelIndex = index; 362 } 363 364 public int compareTo(Object o) { 365 366 int row1 = modelIndex; 367 int row2 = ((Row) o).modelIndex; 368 369 for (Iterator it = sortingColumns.iterator(); it.hasNext(); ) { 370 Directive directive = (Directive) it.next(); 371 int column = directive.column; 372 Object o1 = tableModel.getValueAt(row1, column); 373 Object o2 = tableModel.getValueAt(row2, column); 374 int comparison = 0; 375 376 if (o1 == null && o2 == null) { 378 comparison = 0; 379 } else if (o1 == null) { 380 comparison = -1; 381 } else if (o2 == null) { 382 comparison = 1; 383 } else { 384 comparison = getComparator(column).compare(o1, o2); 385 } 386 387 if (comparison != 0) { 388 return directive.direction == DESCENDING ? -comparison 389 : comparison; 390 } 391 } 392 393 return 0; 394 } 395 } 396 397 private class TableModelHandler implements TableModelListener { 398 399 public void tableChanged(TableModelEvent e) { 400 401 if (!isSorting()) { 403 clearSortingState(); 404 fireTableChanged(e); 405 406 return; 407 } 408 409 if (e == null || e.getFirstRow() == TableModelEvent.HEADER_ROW) { 413 cancelSorting(); 414 fireTableChanged(e); 415 416 return; 417 } 418 419 int column = e.getColumn(); 438 439 if (e.getFirstRow() == e.getLastRow() 440 && column != TableModelEvent.ALL_COLUMNS 441 && getSortingStatus(column) == NOT_SORTED 442 && modelToView != null) { 443 int viewIndex = getModelToView()[e.getFirstRow()]; 444 445 fireTableChanged(new TableModelEvent (TableSorter.this, 446 viewIndex, viewIndex, 447 column, e.getType())); 448 449 return; 450 } 451 452 clearSortingState(); 454 fireTableDataChanged(); 455 456 return; 457 } 458 } 459 460 private class MouseHandler extends MouseAdapter { 461 462 public void mouseClicked(MouseEvent e) { 463 464 JTableHeader h = (JTableHeader ) e.getSource(); 465 TableColumnModel columnModel = h.getColumnModel(); 466 int viewColumn = columnModel.getColumnIndexAtX(e.getX()); 467 int column = columnModel.getColumn(viewColumn).getModelIndex(); 468 469 if (column != -1) { 470 int status = getSortingStatus(column); 471 472 if (!e.isControlDown()) { 473 cancelSorting(); 474 } 475 476 status = status + (e.isShiftDown() ? -1 479 : 1); 480 status = (status + 4) % 3 - 1; 482 setSortingStatus(column, status); 483 } 484 } 485 } 486 487 private static class Arrow implements Icon { 488 489 private boolean descending; 490 private int size; 491 private int priority; 492 493 public Arrow(boolean descending, int size, int priority) { 494 495 this.descending = descending; 496 this.size = size; 497 this.priority = priority; 498 } 499 500 public void paintIcon(Component c, Graphics g, int x, int y) { 501 502 Color color = c == null ? Color.gray 503 : c.getBackground(); 504 505 int dx = (int) (size / 2 * Math.pow(0.8, priority)); 508 int dy = descending ? dx 509 : -dx; 510 511 y = y + 5 * size / 6 + (descending ? -dy 513 : 0); 514 515 int shift = descending ? 1 516 : -1; 517 518 g.translate(x, y); 519 520 g.setColor(color.darker()); 522 g.drawLine(dx / 2, dy, 0, 0); 523 g.drawLine(dx / 2, dy + shift, 0, shift); 524 525 g.setColor(color.brighter()); 527 g.drawLine(dx / 2, dy, dx, 0); 528 g.drawLine(dx / 2, dy + shift, dx, shift); 529 530 if (descending) { 532 g.setColor(color.darker().darker()); 533 } else { 534 g.setColor(color.brighter().brighter()); 535 } 536 537 g.drawLine(dx, 0, 0, 0); 538 g.setColor(color); 539 g.translate(-x, -y); 540 } 541 542 public int getIconWidth() { 543 return size; 544 } 545 546 public int getIconHeight() { 547 return size; 548 } 549 } 550 551 private class SortableHeaderRenderer implements TableCellRenderer { 552 553 private TableCellRenderer tableCellRenderer; 554 555 public SortableHeaderRenderer(TableCellRenderer tableCellRenderer) { 556 this.tableCellRenderer = tableCellRenderer; 557 } 558 559 public Component getTableCellRendererComponent(JTable table, 560 Object value, boolean isSelected, boolean hasFocus, int row, 561 int column) { 562 563 Component c = 564 tableCellRenderer.getTableCellRendererComponent(table, value, 565 isSelected, hasFocus, row, column); 566 567 if (c instanceof JLabel ) { 568 JLabel l = (JLabel ) c; 569 570 l.setHorizontalTextPosition(JLabel.LEFT); 571 572 int modelColumn = table.convertColumnIndexToModel(column); 573 574 l.setIcon(getHeaderRendererIcon(modelColumn, 575 l.getFont().getSize())); 576 } 577 578 return c; 579 } 580 } 581 582 private static class Directive { 583 584 private int column; 585 private int direction; 586 587 public Directive(int column, int direction) { 588 this.column = column; 589 this.direction = direction; 590 } 591 } 592 } 593 | Popular Tags |