1 19 20 package org.netbeans.swing.outline; 21 22 import java.util.ArrayList ; 23 import java.util.Arrays ; 24 import java.util.List ; 25 import javax.swing.SwingUtilities ; 26 import javax.swing.event.TableModelEvent ; 27 import javax.swing.event.TableModelListener ; 28 import javax.swing.event.TreeExpansionEvent ; 29 import javax.swing.event.TreeExpansionListener ; 30 import javax.swing.event.TreeModelEvent ; 31 import javax.swing.event.TreeModelListener ; 32 import javax.swing.table.TableModel ; 33 import javax.swing.tree.AbstractLayoutCache ; 34 import javax.swing.tree.ExpandVetoException ; 35 import javax.swing.tree.TreeModel ; 36 import javax.swing.tree.TreePath ; 37 38 48 final class EventBroadcaster implements TableModelListener , TreeModelListener , ExtTreeWillExpandListener, TreeExpansionListener { 49 50 51 static boolean log = false; 52 53 54 private int logcount = 0; 55 56 57 private DefaultOutlineModel model; 58 59 61 private TreeExpansionEvent inProgressEvent = null; 62 63 67 private TableModelEvent pendingExpansionEvent = null; 68 69 71 private boolean inMultiEvent = false; 72 73 private static final int NODES_CHANGED = 0; 76 private static final int NODES_INSERTED = 1; 77 private static final int NODES_REMOVED = 2; 78 private static final int STRUCTURE_CHANGED = 3; 79 80 private static final String [] types = new String [] { 82 "nodesChanged", "nodesInserted", "nodesRemoved", "structureChanged" 83 }; 85 86 private List tableListeners = new ArrayList (); 87 88 89 private List treeListeners = new ArrayList (); 90 91 92 94 public EventBroadcaster(DefaultOutlineModel model) { 95 setModel (model); 96 } 97 98 99 private void log (String method, Object o) { 100 if (log) { 101 if (o instanceof TableModelEvent ) { 102 o = tableModelEventToString ((TableModelEvent ) o); 104 } 105 System.err.println("EB-" + (logcount++) + " " + method + ":" + 106 (o instanceof String ? 107 (String ) o : o.toString())); 108 } 109 } 110 111 112 122 public boolean areMoreEventsPending() { 123 return inMultiEvent; 124 } 125 126 127 private DefaultOutlineModel getModel() { 128 return model; 129 } 130 131 132 private void setModel(DefaultOutlineModel model) { 133 this.model = model; 134 } 135 136 137 private AbstractLayoutCache getLayout() { 138 return getModel().getLayout(); 139 } 140 141 142 private TreePathSupport getTreePathSupport() { 143 return getModel().getTreePathSupport(); 144 } 145 146 147 private TreeModel getTreeModel() { 148 return getModel().getTreeModel(); 149 } 150 151 154 private TableModel getTableModel() { 155 return getModel().getTableModel(); 156 } 157 158 159 160 162 164 public synchronized void addTableModelListener(TableModelListener l) { 165 tableListeners.add (l); 166 } 167 168 170 public synchronized void addTreeModelListener(TreeModelListener l) { 171 treeListeners.add (l); 172 } 173 174 175 public synchronized void removeTableModelListener(TableModelListener l) { 176 tableListeners.remove(l); 177 } 178 179 180 public synchronized void removeTreeModelListener(TreeModelListener l) { 181 treeListeners.remove(l); 182 } 183 184 186 private void fireTableChange (TableModelEvent e, TableModelListener [] listeners) { 187 if (e == null) { 189 return; 190 } 191 192 assert (e.getSource() == getModel()); 193 194 log ("fireTableChange", e); 195 196 for (int i=0; i < listeners.length; i++) { 197 listeners[i].tableChanged(e); 198 } 199 } 200 201 202 private void fireTableChange (TableModelEvent e) { 203 if (e == null) { 205 return; 206 } 207 inMultiEvent = false; 208 TableModelListener [] listeners = getTableModelListeners(); 209 fireTableChange(e, getTableModelListeners()); 210 } 211 212 214 private void fireTableChange (TableModelEvent [] e) { 215 if (e == null || e.length==0) { 217 return; 218 } 219 220 TableModelListener [] listeners = getTableModelListeners(); 221 inMultiEvent = e.length > 1; 222 try { 223 for (int i=0; i < e.length; i++) { 224 fireTableChange (e[i], listeners); 225 if (i == e.length-1) { 226 inMultiEvent = false; 227 } 228 } 229 } finally { 230 inMultiEvent = false; 231 } 232 } 233 234 235 private TableModelListener [] getTableModelListeners() { 236 TableModelListener [] listeners = null; 237 synchronized (this) { 238 listeners = new TableModelListener [ 239 tableListeners.size()]; 240 241 listeners = (TableModelListener []) 242 tableListeners.toArray(listeners); 243 } 244 return listeners; 245 } 246 247 250 private synchronized void fireTreeChange (TreeModelEvent e, int type) { 251 if (e == null) { 253 return; 254 } 255 assert (e.getSource() == getModel()); 256 257 TreeModelListener [] listeners = null; 258 synchronized (this) { 259 listeners = new TreeModelListener [treeListeners.size()]; 260 listeners = (TreeModelListener []) treeListeners.toArray(listeners); 261 } 262 263 log ("fireTreeChange-" + types[type], e); 264 265 for (int i=0; i < listeners.length; i++) { 267 switch (type) { 268 case NODES_CHANGED : 269 listeners[i].treeNodesChanged(e); 270 break; 271 case NODES_INSERTED : 272 listeners[i].treeNodesInserted(e); 273 break; 274 case NODES_REMOVED : 275 listeners[i].treeNodesRemoved(e); 276 break; 277 case STRUCTURE_CHANGED : 278 listeners[i].treeStructureChanged(e); 279 break; 280 default : 281 assert false; 282 } 283 } 284 } 285 286 288 293 public void tableChanged(TableModelEvent e) { 294 assert SwingUtilities.isEventDispatchThread(); 295 assert (e.getType() == e.UPDATE) : "Table model should only fire " + 300 "updates, never structural changes"; 301 302 fireTableChange (translateEvent(e)); 303 } 304 305 316 public void treeNodesChanged(TreeModelEvent e) { 317 assert SwingUtilities.isEventDispatchThread(); 318 319 fireTreeChange (translateEvent(e), NODES_CHANGED); 320 321 TableModelEvent [] events = translateEvent(e, NODES_CHANGED); 322 getLayout().treeNodesChanged(e); 323 fireTableChange(events); 324 } 325 326 337 public void treeNodesInserted(TreeModelEvent e) { 338 assert SwingUtilities.isEventDispatchThread(); 339 340 fireTreeChange (translateEvent(e), NODES_INSERTED); 341 342 TableModelEvent [] events = translateEvent(e, NODES_INSERTED); 343 getLayout().treeNodesInserted(e); 344 fireTableChange(events); 345 } 346 347 358 public void treeNodesRemoved(TreeModelEvent e) { 359 assert SwingUtilities.isEventDispatchThread(); 360 361 fireTreeChange (e, NODES_REMOVED); 362 363 TableModelEvent [] events = translateEvent(e, NODES_REMOVED); 364 getLayout().treeNodesRemoved(e); 365 fireTableChange(events); 366 } 367 368 371 public void treeStructureChanged(TreeModelEvent e) { 372 assert SwingUtilities.isEventDispatchThread(); 373 374 getLayout().treeStructureChanged(e); 375 fireTreeChange (e, STRUCTURE_CHANGED); 376 377 getTreePathSupport().clear(); 383 384 fireTableChange (new TableModelEvent (getModel())); 386 } 387 388 391 public void treeWillCollapse(TreeExpansionEvent event) throws ExpandVetoException { 392 assert SwingUtilities.isEventDispatchThread(); 393 394 log ("treeWillCollapse", event); 395 396 pendingExpansionEvent = translateEvent (event, false); 400 log ("treeWillCollapse generated ", pendingExpansionEvent); 401 inProgressEvent = event; 402 } 403 404 407 public void treeWillExpand(TreeExpansionEvent event) throws ExpandVetoException { 408 assert SwingUtilities.isEventDispatchThread(); 409 410 log ("treeWillExpand", event); 411 412 pendingExpansionEvent = translateEvent (event, true); 416 417 log ("treeWillExpand generated", pendingExpansionEvent); 418 inProgressEvent = event; 419 } 420 421 public void treeCollapsed(TreeExpansionEvent event) { 422 assert SwingUtilities.isEventDispatchThread(); 423 424 log ("treeExpanded", event); 425 426 if(event != null) { 429 TreePath path = event.getPath(); 430 431 if(path != null && getTreePathSupport().isVisible(path)) { 433 getLayout().setExpandedState(path, false); 434 } 435 } 436 437 438 log ("about to fire", pendingExpansionEvent); 439 440 TreePath path = event.getPath(); 443 int row = getLayout().getRowForPath(path); 444 TableModelEvent evt = new TableModelEvent (getModel(), row, row, 0, 445 TableModelEvent.UPDATE); 446 fireTableChange(new TableModelEvent [] {evt, pendingExpansionEvent}); 447 448 pendingExpansionEvent = null; 449 inProgressEvent = null; 450 } 451 452 454 public void treeExpanded(TreeExpansionEvent event) { 455 assert SwingUtilities.isEventDispatchThread(); 456 457 log ("treeExpanded", event); 458 459 if(event != null) { 462 updateExpandedDescendants(event.getPath()); 463 } 464 465 log ("about to fire", pendingExpansionEvent); 466 467 TreePath path = event.getPath(); 470 int row = getLayout().getRowForPath(path); 471 TableModelEvent evt = new TableModelEvent (getModel(), row, row, 0, 472 TableModelEvent.UPDATE); 473 fireTableChange(new TableModelEvent [] {evt, pendingExpansionEvent}); 474 475 pendingExpansionEvent = null; 476 inProgressEvent = null; 477 } 478 479 482 public void treeExpansionVetoed(TreeExpansionEvent event, ExpandVetoException exception) { 483 assert SwingUtilities.isEventDispatchThread(); 484 485 log ("treeExpansionVetoed", exception); 486 487 if (event == inProgressEvent) { 489 pendingExpansionEvent = null; 494 inProgressEvent = null; 495 } 496 } 497 498 501 503 private void updateExpandedDescendants(TreePath path) { 504 getLayout().setExpandedState(path, true); 505 506 TreePath [] descendants = 507 getTreePathSupport().getExpandedDescendants(path); 508 509 if(descendants.length > 0) { 510 for (int i=0; i < descendants.length; i++) { 511 getLayout().setExpandedState(descendants[i], true); 512 } 513 } 514 } 515 516 517 519 523 private TableModelEvent translateEvent (TableModelEvent e) { 524 TableModelEvent nue = new TableModelEvent (getModel(), 525 e.getFirstRow(), e.getLastRow(), e.getColumn()+1, e.getType()); 526 return nue; 527 } 528 529 531 private TreeModelEvent translateEvent (TreeModelEvent e) { 532 TreeModelEvent nue = new TreeModelEvent (getModel(), e.getPath(), 534 e.getChildIndices(), e.getChildren()); 535 return nue; 536 } 537 538 540 private TableModelEvent [] translateEvent (TreeModelEvent e, int type) { 541 542 TreePath path = e.getTreePath(); 543 int row = getLayout().getRowForPath(path); 544 545 boolean inClosedNode = !getLayout().isExpanded(path); 548 if (inClosedNode) { 549 if (row != -1) { 553 switch (type) { 554 case NODES_CHANGED : 555 case NODES_INSERTED : 556 case NODES_REMOVED : 557 return new TableModelEvent [] { 558 new TableModelEvent (getModel(), row, row, 559 0, TableModelEvent.UPDATE) 560 }; 561 default: 562 assert false : "Unknown event type " + type; 563 } 564 } 565 return new TableModelEvent [0]; 567 } 568 569 boolean discontiguous = isDiscontiguous(e); 570 571 Object [] blocks; 572 if (discontiguous) { 573 blocks = getContiguousIndexBlocks(e, type == NODES_REMOVED); 574 log ("discontiguous " + types[type] + " event", blocks.length + " blocks"); 575 } else { 576 blocks = new Object [] {e.getChildIndices()}; 577 } 578 579 580 TableModelEvent [] result = new TableModelEvent [blocks.length]; 581 for (int i=0; i < blocks.length; i++) { 582 583 int[] currBlock = (int[]) blocks[i]; 584 switch (type) { 585 case NODES_CHANGED : 586 result[i] = createTableChangeEvent (e, currBlock); 587 break; 588 case NODES_INSERTED : 589 result[i] = createTableInsertionEvent (e, currBlock); 590 break; 591 case NODES_REMOVED : 592 result[i] = createTableDeletionEvent (e, currBlock); 593 break; 594 default : 595 assert false : "Unknown event type: " + type; 596 } 597 } 598 log ("translateEvent", e); 599 log ("generated table events", new Integer (result.length)); 600 if (log) { 601 for (int i=0; i < result.length; i++) { 602 log (" Event " + i, result[i]); 603 } 604 } 605 return result; 606 } 607 608 610 private TableModelEvent translateEvent (TreeExpansionEvent e, boolean expand) { 611 616 TreePath path = e.getPath(); 617 618 int firstRow = getLayout().getRowForPath(path) + 1; 620 if (firstRow == -1) { 621 625 return null; 631 } 632 633 TreePath [] paths = getTreePathSupport().getExpandedDescendants(path); 635 636 int count = getTreeModel().getChildCount(path.getLastPathComponent()); 638 639 for (int i=0; i < paths.length; i++) { 641 count += getTreeModel().getChildCount(paths[i].getLastPathComponent()); 642 } 643 644 int lastRow = firstRow + count -1; 646 647 TableModelEvent result = new TableModelEvent (getModel(), firstRow, lastRow, 649 TableModelEvent.ALL_COLUMNS, expand ? TableModelEvent.INSERT : 650 TableModelEvent.DELETE); 651 652 return result; 653 } 654 655 657 private TableModelEvent createTableChangeEvent (TreeModelEvent e, int[] indices) { 658 TableModelEvent result = null; 659 TreePath path = e.getTreePath(); 660 int row = getLayout().getRowForPath(path); 661 662 int first = indices[0]; 663 int last = indices[indices.length-1]; 664 665 result = new TableModelEvent (getModel(), first, last, 668 TableModelEvent.ALL_COLUMNS, TableModelEvent.UPDATE); 669 670 return result; 671 } 672 673 675 private TableModelEvent createTableInsertionEvent (TreeModelEvent e, int[] indices) { 676 TableModelEvent result = null; 677 678 log ("createTableInsertionEvent", e); 679 680 TreePath path = e.getTreePath(); 681 int row = getLayout().getRowForPath(path); 682 683 boolean realInsert = getLayout().isExpanded(path); 684 685 if (realInsert) { 686 if (indices.length == 1) { 687 int affectedRow = row + indices[0] + 1; 692 result = new TableModelEvent (getModel(), affectedRow, affectedRow, 693 TableModelEvent.ALL_COLUMNS, TableModelEvent.INSERT); 694 695 } else { 696 int lowest = indices[0] + 1; 700 int highest = indices[indices.length-1] + 1; 701 result = new TableModelEvent (getModel(), row + lowest, row + highest, 702 TableModelEvent.ALL_COLUMNS, TableModelEvent.INSERT); 703 704 } 705 } else { 706 result = new TableModelEvent (getModel(), row, row, 710 TableModelEvent.ALL_COLUMNS); } 712 return result; 713 } 714 715 716 718 private TableModelEvent createTableDeletionEvent (TreeModelEvent e, int[] indices) { 719 TableModelEvent result = null; 720 721 log ("createTableDeletionEvent " + Arrays.asList(toArrayOfInteger(indices)), e); 722 723 TreePath path = e.getTreePath(); 724 int row = getLayout().getRowForPath(path); 725 if (row == -1) { 726 return null; 728 } 729 730 int countRemoved = indices.length; 731 732 Object [] children = getChildrenForIndices(e, indices); 735 736 for (int i=0; i < children.length; i++) { 737 TreePath childPath = path.pathByAddingChild(children[i]); 738 if (getTreePathSupport().isExpanded(childPath)) { 739 740 int visibleChildren = 741 getLayout().getVisibleChildCount(childPath); 742 743 if (log) { 744 log (childPath + " has ", new Integer (visibleChildren)); 745 } 746 747 countRemoved += visibleChildren; 748 } 749 getTreePathSupport().removePath(path); 750 } 751 752 int firstRow = row + indices[0] + 1; 755 756 log ("firstRow", new Integer (firstRow)); 757 765 System.err.println("Count removed is " + countRemoved); 766 767 int lastRow = firstRow + (countRemoved - 1); 768 769 System.err.println("TableModelEvent: fromRow: " + firstRow + " toRow: " + lastRow); 770 771 result = new TableModelEvent (getModel(), firstRow, lastRow, 772 TableModelEvent.ALL_COLUMNS, TableModelEvent.DELETE); 773 775 871 872 return result; 873 } 874 875 876 878 881 private static boolean isDiscontiguous (TreeModelEvent e) { 882 int[] indices = e.getChildIndices(); 883 if (indices.length == 1) { 884 return false; 885 } 886 Arrays.sort(indices); 887 int lastVal = indices[0]; 888 for (int i=1; i < indices.length; i++) { 889 if (indices[i] != lastVal + 1) { 890 return true; 891 } else { 892 lastVal++; 893 } 894 } 895 return false; 896 } 897 898 906 private static Object [] getContiguousIndexBlocks (TreeModelEvent e, boolean reverseOrder) { 907 int[] indices = e.getChildIndices(); 908 909 if (indices.length == 1) { 911 return new Object [] {indices}; 912 } 913 914 ArrayList al = new ArrayList (); 916 917 if (reverseOrder) { 919 inverseSort (indices); 920 } else { 921 Arrays.sort (indices); 922 } 923 924 925 ArrayList currBlock = new ArrayList (indices.length / 2); 927 al.add(currBlock); 928 929 int lastVal = -1; 932 933 for (int i=0; i < indices.length; i++) { 935 if (i != 0) { 936 boolean newBlock = reverseOrder ? indices[i] != lastVal - 1 : 938 indices[i] != lastVal + 1; 939 940 if (newBlock) { 941 currBlock = new ArrayList (indices.length - 1); 942 al.add(currBlock); 943 } 944 } 945 currBlock.add (new Integer (indices[i])); 946 lastVal = indices[i]; 947 } 948 949 for (int i=0; i < al.size(); i++) { 950 ArrayList curr = (ArrayList ) al.get(i); 951 Integer [] ints = (Integer []) curr.toArray(new Integer [0]); 952 953 al.set(i, toArrayOfInt(ints)); 954 } 955 956 return al.toArray(); 957 } 958 959 961 private Object [] getChildrenForIndices (TreeModelEvent e, int[] indices) { 962 967 Object [] children = e.getChildren(); 970 int[] allIndices = e.getChildIndices(); 971 972 ArrayList al = new ArrayList (); 973 974 for (int i=0; i < indices.length; i++) { 975 int pos = Arrays.binarySearch (allIndices, indices[i]); 976 if (pos > -1) { 977 al.add (children[pos]); 978 } 979 if (al.size() == indices.length) { 980 break; 981 } 982 } 983 return al.toArray(); 984 } 985 986 987 988 private static int[] toArrayOfInt (Integer [] ints) { 989 int[] result = new int[ints.length]; 990 for (int i=0; i < ints.length; i++) { 991 result[i] = ints[i].intValue(); 992 } 993 return result; 994 } 995 996 997 private static Integer [] toArrayOfInteger (int[] ints) { 999 Integer [] result = new Integer [ints.length]; 1000 for (int i=0; i < ints.length; i++) { 1001 result[i] = new Integer (ints[i]); 1002 } 1003 return result; 1004 } 1005 1006 1007 1008 private static void inverseSort (int[] array) { 1009 for (int i=0; i < array.length; i++) { 1012 array[i] *= -1; 1013 } 1014 Arrays.sort(array); 1015 for (int i=0; i < array.length; i++) { 1016 array[i] *= -1; 1017 } 1018 } 1019 1020 private static String tableModelEventToString (TableModelEvent e) { 1021 StringBuffer sb = new StringBuffer (); 1022 sb.append ("TableModelEvent "); 1023 switch (e.getType()) { 1024 case TableModelEvent.INSERT : sb.append ("insert "); 1025 break; 1026 case TableModelEvent.DELETE : sb.append ("delete "); 1027 break; 1028 case TableModelEvent.UPDATE : sb.append ("update "); 1029 break; 1030 default : sb.append ("Unknown type " + e.getType()); 1031 } 1032 sb.append ("from "); 1033 switch (e.getFirstRow()) { 1034 case TableModelEvent.HEADER_ROW : sb.append ("header row "); 1035 break; 1036 default : sb.append (e.getFirstRow()); 1037 sb.append (' '); 1038 } 1039 sb.append ("to "); 1040 sb.append (e.getLastRow()); 1041 sb.append (" column "); 1042 switch (e.getColumn()) { 1043 case TableModelEvent.ALL_COLUMNS : 1044 sb.append ("ALL_COLUMNS"); 1045 break; 1046 default : sb.append (e.getColumn()); 1047 } 1048 return sb.toString(); 1049 } 1050} 1051 | Popular Tags |