1 7 package javax.swing; 8 9 import java.text.Collator ; 10 import java.util.ArrayList ; 11 import java.util.Arrays ; 12 import java.util.Collections ; 13 import java.util.Comparator ; 14 import java.util.List ; 15 import javax.swing.SortOrder ; 16 17 95 public abstract class DefaultRowSorter<M, I> extends RowSorter <M> { 96 99 private boolean sortsOnUpdates; 100 101 104 private Row[] viewToModel; 105 106 109 private int[] modelToView; 110 111 114 private Comparator [] comparators; 115 116 119 private boolean[] isSortable; 120 121 124 private SortKey[] cachedSortKeys; 125 126 129 private Comparator [] sortComparators; 130 131 134 private RowFilter <? super M,? super I> filter; 135 136 140 private FilterEntry filterEntry; 141 142 145 private List <SortKey> sortKeys; 146 147 150 private boolean[] useToString; 151 152 156 private boolean sorted; 157 158 161 private int maxSortKeys; 162 163 166 private ModelWrapper<M,I> modelWrapper; 167 168 172 private int modelRowCount; 173 174 175 178 public DefaultRowSorter() { 179 sortKeys = Collections.emptyList(); 180 maxSortKeys = 3; 181 } 182 183 192 protected final void setModelWrapper(ModelWrapper<M,I> modelWrapper) { 193 if (modelWrapper == null) { 194 throw new IllegalArgumentException ( 195 "modelWrapper most be non-null"); 196 } 197 ModelWrapper<M,I> last = this.modelWrapper; 198 this.modelWrapper = modelWrapper; 199 if (last != null) { 200 modelStructureChanged(); 201 } else { 202 modelRowCount = getModelWrapper().getRowCount(); 205 } 206 } 207 208 215 protected final ModelWrapper<M,I> getModelWrapper() { 216 return modelWrapper; 217 } 218 219 224 public final M getModel() { 225 return getModelWrapper().getModel(); 226 } 227 228 243 public void setSortable(int column, boolean sortable) { 244 checkColumn(column); 245 if (isSortable == null) { 246 isSortable = new boolean[getModelWrapper().getColumnCount()]; 247 for (int i = isSortable.length - 1; i >= 0; i--) { 248 isSortable[i] = true; 249 } 250 } 251 isSortable[column] = sortable; 252 } 253 254 263 public boolean isSortable(int column) { 264 checkColumn(column); 265 return (isSortable == null) ? true : isSortable[column]; 266 } 267 268 281 public void setSortKeys(List <? extends SortKey> sortKeys) { 282 List <SortKey> old = this.sortKeys; 283 if (sortKeys != null && sortKeys.size() > 0) { 284 int max = getModelWrapper().getColumnCount(); 285 for (SortKey key : sortKeys) { 286 if (key == null || key.getColumn() < 0 || 287 key.getColumn() >= max) { 288 throw new IllegalArgumentException ("Invalid SortKey"); 289 } 290 } 291 this.sortKeys = Collections.unmodifiableList( 292 new ArrayList <SortKey>(sortKeys)); 293 } 294 else { 295 this.sortKeys = Collections.emptyList(); 296 } 297 if (!this.sortKeys.equals(old)) { 298 fireSortOrderChanged(); 299 if (viewToModel == null) { 300 sort(); 303 } else { 304 sortExistingData(); 305 } 306 } 307 } 308 309 317 public List <? extends SortKey> getSortKeys() { 318 return sortKeys; 319 } 320 321 349 public void setMaxSortKeys(int max) { 350 if (max < 1) { 351 throw new IllegalArgumentException ("Invalid max"); 352 } 353 maxSortKeys = max; 354 } 355 356 361 public int getMaxSortKeys() { 362 return maxSortKeys; 363 } 364 365 374 public void setSortsOnUpdates(boolean sortsOnUpdates) { 375 this.sortsOnUpdates = sortsOnUpdates; 376 } 377 378 384 public boolean getSortsOnUpdates() { 385 return sortsOnUpdates; 386 } 387 388 405 public void setRowFilter(RowFilter <? super M,? super I> filter) { 406 this.filter = filter; 407 sort(); 408 } 409 410 416 public RowFilter <? super M,? super I> getRowFilter() { 417 return filter; 418 } 419 420 434 public void toggleSortOrder(int column) { 435 checkColumn(column); 436 if (isSortable(column)) { 437 List <SortKey> keys = new ArrayList <SortKey>(getSortKeys()); 438 SortKey sortKey; 439 int sortIndex; 440 for (sortIndex = keys.size() - 1; sortIndex >= 0; sortIndex--) { 441 if (keys.get(sortIndex).getColumn() == column) { 442 break; 443 } 444 } 445 if (sortIndex == -1) { 446 sortKey = new SortKey(column, SortOrder.ASCENDING); 448 keys.add(0, sortKey); 449 } 450 else if (sortIndex == 0) { 451 keys.set(0, toggle(keys.get(0))); 453 } 454 else { 455 keys.remove(sortIndex); 458 keys.add(0, new SortKey(column, SortOrder.ASCENDING)); 459 } 460 if (keys.size() > getMaxSortKeys()) { 461 keys = keys.subList(0, getMaxSortKeys()); 462 } 463 setSortKeys(keys); 464 } 465 } 466 467 private SortKey toggle(SortKey key) { 468 if (key.getSortOrder() == SortOrder.ASCENDING) { 469 return new SortKey(key.getColumn(), SortOrder.DESCENDING); 470 } 471 return new SortKey(key.getColumn(), SortOrder.ASCENDING); 472 } 473 474 479 public int convertRowIndexToView(int index) { 480 if (modelToView == null) { 481 if (index < 0 || index >= getModelWrapper().getRowCount()) { 482 throw new IndexOutOfBoundsException ("Invalid index"); 483 } 484 return index; 485 } 486 return modelToView[index]; 487 } 488 489 494 public int convertRowIndexToModel(int index) { 495 if (viewToModel == null) { 496 if (index < 0 || index >= getModelWrapper().getRowCount()) { 497 throw new IndexOutOfBoundsException ("Invalid index"); 498 } 499 return index; 500 } 501 return viewToModel[index].modelIndex; 502 } 503 504 private boolean isUnsorted() { 505 List <? extends SortKey> keys = getSortKeys(); 506 int keySize = keys.size(); 507 return (keySize == 0 || keys.get(0).getSortOrder() == 508 SortOrder.UNSORTED); 509 } 510 511 515 private void sortExistingData() { 516 int[] lastViewToModel = getViewToModelAsInts(viewToModel); 517 518 updateUseToString(); 519 cacheSortKeys(getSortKeys()); 520 521 if (isUnsorted()) { 522 if (getRowFilter() == null) { 523 viewToModel = null; 524 modelToView = null; 525 } else { 526 int included = 0; 527 for (int i = 0; i < modelToView.length; i++) { 528 if (modelToView[i] != -1) { 529 viewToModel[included].modelIndex = i; 530 modelToView[i] = included++; 531 } 532 } 533 } 534 } else { 535 Arrays.sort(viewToModel); 537 538 setModelToViewFromViewToModel(false); 540 } 541 fireRowSorterChanged(lastViewToModel); 542 } 543 544 553 public void sort() { 554 sorted = true; 555 int[] lastViewToModel = getViewToModelAsInts(viewToModel); 556 updateUseToString(); 557 if (isUnsorted()) { 558 cachedSortKeys = new SortKey[0]; 560 if (getRowFilter() == null) { 561 if (viewToModel != null) { 563 viewToModel = null; 565 modelToView = null; 566 } 567 else { 568 return; 571 } 572 } 573 else { 574 initializeFilteredMapping(); 576 } 577 } 578 else { 579 cacheSortKeys(getSortKeys()); 580 581 if (getRowFilter() != null) { 582 initializeFilteredMapping(); 583 } 584 else { 585 createModelToView(getModelWrapper().getRowCount()); 586 createViewToModel(getModelWrapper().getRowCount()); 587 } 588 589 Arrays.sort(viewToModel); 591 592 setModelToViewFromViewToModel(false); 594 } 595 fireRowSorterChanged(lastViewToModel); 596 } 597 598 601 private void updateUseToString() { 602 int i = getModelWrapper().getColumnCount(); 603 if (useToString == null || useToString.length != i) { 604 useToString = new boolean[i]; 605 } 606 for (--i; i >= 0; i--) { 607 useToString[i] = useToString(i); 608 } 609 } 610 611 615 private void initializeFilteredMapping() { 616 int rowCount = getModelWrapper().getRowCount(); 617 int i, j; 618 int excludedCount = 0; 619 620 createModelToView(rowCount); 622 for (i = 0; i < rowCount; i++) { 623 if (include(i)) { 624 modelToView[i] = i - excludedCount; 625 } 626 else { 627 modelToView[i] = -1; 628 excludedCount++; 629 } 630 } 631 632 createViewToModel(rowCount - excludedCount); 634 for (i = 0, j = 0; i < rowCount; i++) { 635 if (modelToView[i] != -1) { 636 viewToModel[j++].modelIndex = i; 637 } 638 } 639 } 640 641 644 private void createModelToView(int rowCount) { 645 if (modelToView == null || modelToView.length != rowCount) { 646 modelToView = new int[rowCount]; 647 } 648 } 649 650 653 private void createViewToModel(int rowCount) { 654 int recreateFrom = 0; 655 if (viewToModel != null) { 656 recreateFrom = Math.min(rowCount, viewToModel.length); 657 if (viewToModel.length != rowCount) { 658 Row[] oldViewToModel = viewToModel; 659 viewToModel = new Row[rowCount]; 660 System.arraycopy(oldViewToModel, 0, viewToModel, 661 0, recreateFrom); 662 } 663 } 664 else { 665 viewToModel = new Row[rowCount]; 666 } 667 int i; 668 for (i = 0; i < recreateFrom; i++) { 669 viewToModel[i].modelIndex = i; 670 } 671 for (i = recreateFrom; i < rowCount; i++) { 672 viewToModel[i] = new Row(this, i); 673 } 674 } 675 676 679 private void cacheSortKeys(List <? extends SortKey> keys) { 680 int keySize = keys.size(); 681 sortComparators = new Comparator [keySize]; 682 for (int i = 0; i < keySize; i++) { 683 sortComparators[i] = getComparator0(keys.get(i).getColumn()); 684 } 685 cachedSortKeys = keys.toArray(new SortKey[keySize]); 686 } 687 688 700 protected boolean useToString(int column) { 701 return (getComparator(column) == null); 702 } 703 704 709 private void setModelToViewFromViewToModel(boolean unsetFirst) { 710 int i; 711 if (unsetFirst) { 712 for (i = modelToView.length - 1; i >= 0; i--) { 713 modelToView[i] = -1; 714 } 715 } 716 for (i = viewToModel.length - 1; i >= 0; i--) { 717 modelToView[viewToModel[i].modelIndex] = i; 718 } 719 } 720 721 private int[] getViewToModelAsInts(Row[] viewToModel) { 722 if (viewToModel != null) { 723 int[] viewToModelI = new int[viewToModel.length]; 724 for (int i = viewToModel.length - 1; i >= 0; i--) { 725 viewToModelI[i] = viewToModel[i].modelIndex; 726 } 727 return viewToModelI; 728 } 729 return new int[0]; 730 } 731 732 743 public void setComparator(int column, Comparator <?> comparator) { 744 checkColumn(column); 745 if (comparators == null) { 746 comparators = new Comparator [getModelWrapper().getColumnCount()]; 747 } 748 comparators[column] = comparator; 749 } 750 751 762 public Comparator <?> getComparator(int column) { 763 checkColumn(column); 764 if (comparators != null) { 765 return comparators[column]; 766 } 767 return null; 768 } 769 770 private Comparator getComparator0(int column) { 773 Comparator comparator = getComparator(column); 774 if (comparator != null) { 775 return comparator; 776 } 777 return Collator.getInstance(); 780 } 781 782 private RowFilter.Entry <M,I> getFilterEntry(int modelIndex) { 783 if (filterEntry == null) { 784 filterEntry = new FilterEntry(); 785 } 786 filterEntry.modelIndex = modelIndex; 787 return filterEntry; 788 } 789 790 793 public int getViewRowCount() { 794 if (viewToModel != null) { 795 return viewToModel.length; 797 } 798 return getModelWrapper().getRowCount(); 799 } 800 801 804 public int getModelRowCount() { 805 return getModelWrapper().getRowCount(); 806 } 807 808 private void allChanged() { 809 modelToView = null; 810 viewToModel = null; 811 comparators = null; 812 isSortable = null; 813 if (isUnsorted()) { 814 sort(); 817 } else { 818 setSortKeys(null); 819 } 820 } 821 822 825 public void modelStructureChanged() { 826 allChanged(); 827 modelRowCount = getModelWrapper().getRowCount(); 828 } 829 830 833 public void allRowsChanged() { 834 modelRowCount = getModelWrapper().getRowCount(); 835 sort(); 836 } 837 838 843 public void rowsInserted(int firstRow, int endRow) { 844 checkAgainstModel(firstRow, endRow); 845 int newModelRowCount = getModelWrapper().getRowCount(); 846 if (endRow >= newModelRowCount) { 847 throw new IndexOutOfBoundsException ("Invalid range"); 848 } 849 modelRowCount = newModelRowCount; 850 if (shouldOptimizeChange(firstRow, endRow)) { 851 rowsInserted0(firstRow, endRow); 852 } 853 } 854 855 860 public void rowsDeleted(int firstRow, int endRow) { 861 checkAgainstModel(firstRow, endRow); 862 if (firstRow >= modelRowCount || endRow >= modelRowCount) { 863 throw new IndexOutOfBoundsException ("Invalid range"); 864 } 865 modelRowCount = getModelWrapper().getRowCount(); 866 if (shouldOptimizeChange(firstRow, endRow)) { 867 rowsDeleted0(firstRow, endRow); 868 } 869 } 870 871 876 public void rowsUpdated(int firstRow, int endRow) { 877 checkAgainstModel(firstRow, endRow); 878 if (firstRow >= modelRowCount || endRow >= modelRowCount) { 879 throw new IndexOutOfBoundsException ("Invalid range"); 880 } 881 if (getSortsOnUpdates()) { 882 if (shouldOptimizeChange(firstRow, endRow)) { 883 rowsUpdated0(firstRow, endRow); 884 } 885 } 886 else { 887 sorted = false; 888 } 889 } 890 891 896 public void rowsUpdated(int firstRow, int endRow, int column) { 897 checkColumn(column); 898 rowsUpdated(firstRow, endRow); 899 } 900 901 private void checkAgainstModel(int firstRow, int endRow) { 902 if (firstRow > endRow || firstRow < 0 || endRow < 0 || 903 firstRow > modelRowCount) { 904 throw new IndexOutOfBoundsException ("Invalid range"); 905 } 906 } 907 908 911 private boolean include(int row) { 912 RowFilter <? super M, ? super I> filter = getRowFilter(); 913 if (filter != null) { 914 return filter.include(getFilterEntry(row)); 915 } 916 return true; 918 } 919 920 @SuppressWarnings ("unchecked") 921 private int compare(int model1, int model2) { 922 int column; 923 SortOrder sortOrder; 924 Object v1, v2; 925 int result; 926 927 for (int counter = 0; counter < cachedSortKeys.length; counter++) { 928 column = cachedSortKeys[counter].getColumn(); 929 sortOrder = cachedSortKeys[counter].getSortOrder(); 930 if (sortOrder == SortOrder.UNSORTED) { 931 result = model1 - model2; 932 } else { 933 if (useToString[column]) { 935 v1 = getModelWrapper().getStringValueAt(model1, column); 936 v2 = getModelWrapper().getStringValueAt(model2, column); 937 } else { 938 v1 = getModelWrapper().getValueAt(model1, column); 939 v2 = getModelWrapper().getValueAt(model2, column); 940 } 941 if (v1 == null) { 943 if (v2 == null) { 944 result = 0; 945 } else { 946 result = -1; 947 } 948 } else if (v2 == null) { 949 result = 1; 950 } else { 951 result = sortComparators[counter].compare(v1, v2); 952 } 953 if (sortOrder == SortOrder.DESCENDING) { 954 result *= -1; 955 } 956 } 957 if (result != 0) { 958 return result; 959 } 960 } 961 return model1 - model2; 963 } 964 965 968 private boolean isTransformed() { 969 return (viewToModel != null); 970 } 971 972 978 private void insertInOrder(List <Row> toAdd, Row[] current) { 979 int last = 0; 980 int index; 981 int max = toAdd.size(); 982 for (int i = 0; i < max; i++) { 983 index = Arrays.binarySearch(current, toAdd.get(i)); 984 if (index < 0) { 985 index = -1 - index; 986 } 987 System.arraycopy(current, last, 988 viewToModel, last + i, index - last); 989 viewToModel[index + i] = toAdd.get(i); 990 last = index; 991 } 992 System.arraycopy(current, last, viewToModel, last + max, 993 current.length - last); 994 } 995 996 1001 private boolean shouldOptimizeChange(int firstRow, int lastRow) { 1002 if (!isTransformed()) { 1003 return false; 1005 } 1006 if (!sorted || (lastRow - firstRow) > viewToModel.length / 10) { 1007 sort(); 1009 return false; 1010 } 1011 return true; 1012 } 1013 1014 private void rowsInserted0(int firstRow, int lastRow) { 1015 int[] oldViewToModel = getViewToModelAsInts(viewToModel); 1016 int i; 1017 int delta = (lastRow - firstRow) + 1; 1018 List <Row> added = new ArrayList <Row>(delta); 1019 1020 for (i = firstRow; i <= lastRow; i++) { 1022 if (include(i)) { 1023 added.add(new Row(this, i)); 1024 } 1025 } 1026 1027 int viewIndex; 1029 for (i = modelToView.length - 1; i >= firstRow; i--) { 1030 viewIndex = modelToView[i]; 1031 if (viewIndex != -1) { 1032 viewToModel[viewIndex].modelIndex += delta; 1033 } 1034 } 1035 1036 if (added.size() > 0) { 1038 Collections.sort(added); 1039 Row[] lastViewToModel = viewToModel; 1040 viewToModel = new Row[viewToModel.length + added.size()]; 1041 insertInOrder(added, lastViewToModel); 1042 } 1043 1044 createModelToView(getModelWrapper().getRowCount()); 1046 setModelToViewFromViewToModel(true); 1047 1048 fireRowSorterChanged(oldViewToModel); 1050 } 1051 1052 private void rowsDeleted0(int firstRow, int lastRow) { 1053 int[] oldViewToModel = getViewToModelAsInts(viewToModel); 1054 int removedFromView = 0; 1055 int i; 1056 int viewIndex; 1057 1058 for (i = firstRow; i <= lastRow; i++) { 1060 viewIndex = modelToView[i]; 1061 if (viewIndex != -1) { 1062 removedFromView++; 1063 viewToModel[viewIndex] = null; 1064 } 1065 } 1066 1067 int delta = lastRow - firstRow + 1; 1069 for (i = modelToView.length - 1; i > lastRow; i--) { 1070 viewIndex = modelToView[i]; 1071 if (viewIndex != -1) { 1072 viewToModel[viewIndex].modelIndex -= delta; 1073 } 1074 } 1075 1076 if (removedFromView > 0) { 1078 Row[] newViewToModel = new Row[viewToModel.length - 1079 removedFromView]; 1080 int newIndex = 0; 1081 int last = 0; 1082 for (i = 0; i < viewToModel.length; i++) { 1083 if (viewToModel[i] == null) { 1084 System.arraycopy(viewToModel, last, 1085 newViewToModel, newIndex, i - last); 1086 newIndex += (i - last); 1087 last = i + 1; 1088 } 1089 } 1090 System.arraycopy(viewToModel, last, 1091 newViewToModel, newIndex, viewToModel.length - last); 1092 viewToModel = newViewToModel; 1093 } 1094 1095 createModelToView(getModelWrapper().getRowCount()); 1097 setModelToViewFromViewToModel(true); 1098 1099 fireRowSorterChanged(oldViewToModel); 1101 } 1102 1103 private void rowsUpdated0(int firstRow, int lastRow) { 1104 int[] oldViewToModel = getViewToModelAsInts(viewToModel); 1105 int i, j; 1106 int delta = lastRow - firstRow + 1; 1107 int modelIndex; 1108 int last; 1109 int index; 1110 1111 if (getRowFilter() == null) { 1112 1114 Row[] updated = new Row[delta]; 1116 for (j = 0, i = firstRow; i <= lastRow; i++, j++) { 1117 updated[j] = viewToModel[modelToView[i]]; 1118 } 1119 1120 Arrays.sort(updated); 1122 1123 Row[] intermediary = new Row[viewToModel.length - delta]; 1126 for (i = 0, j = 0; i < viewToModel.length; i++) { 1127 modelIndex = viewToModel[i].modelIndex; 1128 if (modelIndex < firstRow || modelIndex > lastRow) { 1129 intermediary[j++] = viewToModel[i]; 1130 } 1131 } 1132 1133 insertInOrder(Arrays.asList(updated), intermediary); 1135 1136 setModelToViewFromViewToModel(false); 1138 } 1139 else { 1140 1142 List <Row> updated = new ArrayList <Row>(delta); 1145 int newlyVisible = 0; 1146 int newlyHidden = 0; 1147 int effected = 0; 1148 for (i = firstRow; i <= lastRow; i++) { 1149 if (modelToView[i] == -1) { 1150 if (include(i)) { 1152 updated.add(new Row(this, i)); 1154 newlyVisible++; 1155 } 1156 } 1157 else { 1158 if (!include(i)) { 1161 newlyHidden++; 1162 } 1163 else { 1164 updated.add(viewToModel[modelToView[i]]); 1165 } 1166 modelToView[i] = -2; 1167 effected++; 1168 } 1169 } 1170 1171 Collections.sort(updated); 1173 1174 Row[] intermediary = new Row[viewToModel.length - effected]; 1177 for (i = 0, j = 0; i < viewToModel.length; i++) { 1178 modelIndex = viewToModel[i].modelIndex; 1179 if (modelToView[modelIndex] != -2) { 1180 intermediary[j++] = viewToModel[i]; 1181 } 1182 } 1183 1184 if (newlyVisible != newlyHidden) { 1186 viewToModel = new Row[viewToModel.length + newlyVisible - 1187 newlyHidden]; 1188 } 1189 1190 insertInOrder(updated, intermediary); 1192 1193 setModelToViewFromViewToModel(true); 1195 } 1196 fireRowSorterChanged(oldViewToModel); 1198 } 1199 1200 private void checkColumn(int column) { 1201 if (column < 0 || column >= getModelWrapper().getColumnCount()) { 1202 throw new IndexOutOfBoundsException ( 1203 "column beyond range of TableModel"); 1204 } 1205 } 1206 1207 1208 1230 protected abstract static class ModelWrapper<M,I> { 1231 1234 protected ModelWrapper() { 1235 } 1236 1237 1243 public abstract M getModel(); 1244 1245 1250 public abstract int getColumnCount(); 1251 1252 1257 public abstract int getRowCount(); 1258 1259 1268 public abstract Object getValueAt(int row, int column); 1269 1270 1283 public String getStringValueAt(int row, int column) { 1284 Object o = getValueAt(row, column); 1285 if (o == null) { 1286 return ""; 1287 } 1288 String string = o.toString(); 1289 if (string == null) { 1290 return ""; 1291 } 1292 return string; 1293 } 1294 1295 1306 public abstract I getIdentifier(int row); 1307 } 1308 1309 1310 1316 private class FilterEntry extends RowFilter.Entry <M,I> { 1317 1320 int modelIndex; 1321 1322 public M getModel() { 1323 return getModelWrapper().getModel(); 1324 } 1325 1326 public int getValueCount() { 1327 return getModelWrapper().getColumnCount(); 1328 } 1329 1330 public Object getValue(int index) { 1331 return getModelWrapper().getValueAt(modelIndex, index); 1332 } 1333 1334 public String getStringValue(int index) { 1335 return getModelWrapper().getStringValueAt(modelIndex, index); 1336 } 1337 1338 public I getIdentifier() { 1339 return getModelWrapper().getIdentifier(modelIndex); 1340 } 1341 } 1342 1343 1344 1348 private static class Row implements Comparable <Row> { 1350 private DefaultRowSorter sorter; 1351 int modelIndex; 1352 1353 public Row(DefaultRowSorter sorter, int index) { 1354 this.sorter = sorter; 1355 modelIndex = index; 1356 } 1357 1358 public int compareTo(Row o) { 1359 return sorter.compare(modelIndex, o.modelIndex); 1360 } 1361 } 1362} 1363 | Popular Tags |