1 19 package org.openide.explorer; 20 21 import org.openide.nodes.*; 22 import org.openide.util.*; 23 import org.openide.util.io.SafeException; 24 25 import java.awt.Component ; 26 27 import java.beans.*; 28 29 import java.io.*; 30 31 import java.util.*; 32 33 34 65 public final class ExplorerManager extends Object implements Serializable, Cloneable { 66 67 static final long serialVersionUID = -4330330689803575792L; 68 69 70 public static final String PROP_ROOT_CONTEXT = "rootContext"; 72 73 public static final String PROP_EXPLORED_CONTEXT = "exploredContext"; 75 76 public static final String PROP_SELECTED_NODES = "selectedNodes"; 78 79 public static final String PROP_NODE_CHANGE = "nodeChange"; 81 83 static RequestProcessor selectionProcessor; 84 85 88 private static final int SELECTION_SYNC_DELAY = 200; 89 90 92 private static final ObjectStreamField[] serialPersistentFields = { 93 new ObjectStreamField("root", Node.Handle.class), new ObjectStreamField("rootName", String .class), new ObjectStreamField("explored", String [].class), new ObjectStreamField("selected", Object [].class) }; 98 99 100 private transient VetoableChangeSupport vetoableSupport; 101 102 103 private transient PropertyChangeSupport propertySupport; 104 105 106 private Node rootContext; 107 108 109 private Node exploredContext; 110 111 112 private Node[] selectedNodes; 113 114 115 private transient Listener listener; 116 117 118 private transient NodeListener weakListener; 119 120 122 private RequestProcessor.Task selectionSyncTask; 123 124 125 private ExplorerActionsImpl actions; 126 127 128 public ExplorerManager() { 129 init(); 130 } 131 132 134 private void init() { 135 exploredContext = rootContext = Node.EMPTY; 136 selectedNodes = new Node[0]; 137 listener = new Listener(); 138 weakListener = NodeOp.weakNodeListener(listener, null); 139 } 140 141 144 public Object clone() { 145 ExplorerManager em = new ExplorerManager(); 146 em.rootContext = rootContext; 147 em.exploredContext = exploredContext; 148 em.selectedNodes = selectedNodes; 149 150 return em; 151 } 152 153 156 public Node[] getSelectedNodes() { 157 return selectedNodes; 158 } 159 160 private boolean equalNodes(Node[] arr1, Node[] arr2) { 162 if (!Arrays.equals(arr1, arr2)) { 164 return false; 165 } 166 167 if ((arr1 == null) || (arr1.length == 0)) { 168 return true; 169 } 170 171 int i = 0; 173 174 while ((i < arr1.length) && Arrays.equals(NodeOp.createPath(arr1[i], null), NodeOp.createPath(arr2[i], null))) { 175 i++; 176 } 177 178 return i == arr1.length; 179 } 180 181 187 public final void setSelectedNodes(final Node[] value) 188 throws PropertyVetoException { 189 class AtomicSetSelectedNodes implements Runnable { 190 public PropertyVetoException veto; 191 private boolean doFire; 192 private Node[] oldValue; 193 194 195 private boolean checkArgumentIsValid() { 196 if (value == null) { 197 throw new IllegalArgumentException (getString("EXC_NodeCannotBeNull")); 198 } 199 200 if (equalNodes(value, selectedNodes)) { 201 return false; 202 } 203 204 for (int i = 0; i < value.length; i++) { 205 if (value[i] == null) { 206 throw new IllegalArgumentException (getString("EXC_NoElementOfNodeSelectionMayBeNull")); 207 } 208 209 checkUnderRoot(value[i], "EXC_NodeSelectionCannotContainNodes"); 210 } 211 212 if ((value.length != 0) && (vetoableSupport != null)) { 213 try { 214 vetoableSupport.fireVetoableChange(PROP_SELECTED_NODES, selectedNodes, value); 216 } catch (PropertyVetoException ex) { 217 veto = ex; 218 219 return false; 220 } 221 } 222 223 return true; 224 } 225 226 private void updateSelection() { 227 oldValue = selectedNodes; 228 229 Collection<Node> currentNodes = Arrays.asList(oldValue); 230 231 Collection<Node> newSelection = Arrays.asList(value); 233 234 Collection<Node> nodesToAdd = new LinkedList<Node>(newSelection); 235 nodesToAdd.removeAll(currentNodes); 236 237 Collection<Node> nodesToRemove = new LinkedList<Node>(currentNodes); 238 nodesToRemove.removeAll(newSelection); 239 240 selectedNodes = value; 241 242 Iterator<Node> it; 243 244 for (it = nodesToRemove.iterator(); it.hasNext();) { 246 Node n = it.next(); 247 n.removeNodeListener(weakListener); 248 } 249 250 for (it = nodesToAdd.iterator(); it.hasNext();) { 252 Node n = it.next(); 253 n.removeNodeListener(weakListener); 254 n.addNodeListener(weakListener); 255 } 256 257 doFire = true; 258 } 259 260 public void fire() { 261 if (doFire) { 262 fireInAWT(PROP_SELECTED_NODES, oldValue, selectedNodes); 263 } 264 } 265 266 public void run() { 267 if (checkArgumentIsValid()) { 268 updateSelection(); 269 } 270 } 271 } 272 273 AtomicSetSelectedNodes setNodes = new AtomicSetSelectedNodes(); 274 Children.MUTEX.readAccess(setNodes); 275 setNodes.fire(); 276 277 if (setNodes.veto != null) { 278 throw setNodes.veto; 279 } 280 } 281 282 291 public final Node getExploredContext() { 292 return exploredContext; 293 } 294 295 300 public final void setExploredContext(Node value) { 301 setExploredContext(value, new Node[0]); 302 } 303 304 310 public final void setExploredContext(final Node value, final Node[] selection) { 311 class SetExploredContext implements Runnable { 312 boolean doFire; 313 Object oldValue; 314 315 public void run() { 316 if (Utilities.compareObjects(value, exploredContext)) { 318 setSelectedNodes0(selection); 319 320 return; 321 } 322 323 checkUnderRoot(value, "EXC_ContextMustBeWithinRootContext"); 324 setSelectedNodes0(selection); 325 326 oldValue = exploredContext; 327 exploredContext = value; 328 329 doFire = true; 330 } 331 public void fire() { 332 if (doFire) { 333 fireInAWT(PROP_EXPLORED_CONTEXT, oldValue, value); 334 } 335 } 336 } 337 338 SetExploredContext set = new SetExploredContext(); 339 Children.MUTEX.readAccess(set); 340 set.fire(); 341 } 342 343 350 public final void setExploredContextAndSelection(final Node value, final Node[] selection) 351 throws PropertyVetoException { 352 class SetExploredContextAndSelection implements Runnable { 353 public PropertyVetoException veto; 354 private boolean doFire; 355 private Object oldValue; 356 357 public void run() { 358 try { 359 if (Utilities.compareObjects(value, exploredContext)) { 361 setSelectedNodes(selection); 362 363 return; 364 } 365 366 checkUnderRoot(value, "EXC_ContextMustBeWithinRootContext"); 367 setSelectedNodes(selection); 368 369 oldValue = exploredContext; 370 exploredContext = value; 371 372 doFire = true; 373 } catch (PropertyVetoException ex) { 374 veto = ex; 375 } 376 } 377 378 public void fire() { 379 if (doFire) { 380 fireInAWT(PROP_EXPLORED_CONTEXT, oldValue, exploredContext); 381 } 382 } 383 } 384 385 SetExploredContextAndSelection set = new SetExploredContextAndSelection(); 386 Children.MUTEX.readAccess(set); 387 set.fire(); 388 389 if (set.veto != null) { 390 throw set.veto; 391 } 392 } 393 394 395 final void setSelectedNodes0(Node[] nodes) { 396 try { 397 setSelectedNodes(nodes); 398 } catch (PropertyVetoException e) { 399 } 400 } 401 402 412 public final Node getRootContext() { 413 return rootContext; 414 } 415 416 422 public final void setRootContext(Node value) { 423 if (value == null) { 424 throw new IllegalArgumentException (getString("EXC_CannotHaveNullRootContext")); 425 } 426 427 if (rootContext.equals(value)) { 428 return; 429 } 430 431 Node oldValue = rootContext; 432 rootContext = value; 433 434 oldValue.removeNodeListener(weakListener); 435 rootContext.addNodeListener(weakListener); 436 437 fireInAWT(PROP_ROOT_CONTEXT, oldValue, rootContext); 438 439 Node[] newselection = getSelectedNodes(); 440 441 if (!areUnderTarget(newselection, rootContext)) { 442 newselection = new Node[0]; 443 } 444 445 setExploredContext(rootContext, newselection); 446 } 447 448 449 private boolean areUnderTarget(Node[] nodes, Node target) { 450 bigloop: 451 for (int i = 0; i < nodes.length; i++) { 452 Node node = nodes[i]; 453 454 while (node != null) { 455 if (node.equals(target)) { 456 continue bigloop; 457 } 458 459 node = node.getParentNode(); 460 } 461 462 return false; 463 } 464 465 return true; 466 } 467 468 471 public synchronized void addPropertyChangeListener(PropertyChangeListener l) { 472 if (propertySupport == null) { 473 propertySupport = new PropertyChangeSupport(this); 474 } 475 476 propertySupport.addPropertyChangeListener(l); 477 } 478 479 482 public synchronized void removePropertyChangeListener(PropertyChangeListener l) { 483 if (propertySupport != null) { 484 propertySupport.removePropertyChangeListener(l); 485 } 486 } 487 488 491 public synchronized void addVetoableChangeListener(VetoableChangeListener l) { 492 if (vetoableSupport == null) { 493 vetoableSupport = new VetoableChangeSupport(this); 494 } 495 496 vetoableSupport.addVetoableChangeListener(l); 497 } 498 499 502 public synchronized void removeVetoableChangeListener(VetoableChangeListener l) { 503 if (vetoableSupport != null) { 504 vetoableSupport.removeVetoableChangeListener(l); 505 } 506 } 507 508 511 private boolean isUnderRoot(Node node) { 512 while (node != null) { 513 if (node.equals(rootContext)) { 514 return true; 515 } 516 517 node = node.getParentNode(); 518 } 519 520 return false; 521 } 522 523 526 private void checkUnderRoot(Node value, String errorKey) { 527 if ((value != null) && !isUnderRoot(value)) { 528 throw new IllegalArgumentException ( 529 NbBundle.getMessage( 530 ExplorerManager.class, errorKey, value.getDisplayName(), rootContext.getDisplayName() 531 ) 532 ); 533 } 534 } 535 536 538 final void waitFinished() { 539 if (selectionSyncTask != null) { 540 selectionSyncTask.waitFinished(); 541 } 542 } 543 544 561 private void writeObject(ObjectOutputStream os) throws IOException { 562 os.writeObject(this); 564 565 ObjectOutputStream.PutField fields = os.putFields(); 566 567 Node.Handle rCH = rootContext.getHandle(); 571 fields.put("root", rCH); 573 fields.put("rootName", rootContext.getDisplayName()); 576 if (rCH != null) { 577 String [] explored; 584 585 if (exploredContext == null) { 586 explored = null; 587 } else if (isUnderRoot(exploredContext)) { 588 explored = NodeOp.createPath(exploredContext, rootContext); 589 } else { 590 explored = null; 591 } 592 593 fields.put("explored", explored); 595 List<String []> selected = new LinkedList<String []>(); 596 597 for (int i = 0; i < selectedNodes.length; i++) { 598 if (isUnderRoot(selectedNodes[i])) { 599 selected.add(NodeOp.createPath(selectedNodes[i], rootContext)); 600 } 601 } 602 603 fields.put("selected", selected.toArray()); } 605 606 os.writeFields(); 607 } 608 609 612 private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { 613 init(); 615 616 Object firstObject = ois.readObject(); 618 619 if (firstObject != this) { 620 readObjectOld((Node.Handle) firstObject, ois); 622 623 return; 624 } 625 626 ObjectInputStream.GetField fields = ois.readFields(); 628 629 Node.Handle h = (Node.Handle) fields.get("root", null); 632 final String rootName = (String ) fields.get("rootName", null); 635 if (h == null) { 637 throw new SafeException( 640 new IOException( 641 "Could not restore Explorer window; the root node \"" + rootName + 642 "\" is not persistent; override Node.getHandle to fix" 643 ) 644 ); } else { 646 String [] exploredCtx = (String []) fields.get("explored", null); Object [] selPaths = (Object []) fields.get("selected", null); 649 try { 650 Node root = h.getNode(); 651 652 if (root == null) { 653 throw new IOException("Node.Handle.getNode (for " + rootName + ") should not return null"); } 655 656 restoreSelection(root, exploredCtx, Arrays.asList(selPaths)); 657 } catch (IOException ioe) { 658 SafeException safe = new SafeException(ioe); 659 660 if (!Utilities.compareObjects(ioe.getMessage(), ioe.getLocalizedMessage())) { 661 Exceptions.attachLocalizedMessage(safe, 662 NbBundle.getMessage(ExplorerManager.class, 663 "EXC_handle_failed", 664 rootName)); 665 } 666 667 throw safe; 668 } 669 } 670 } 671 672 private void readObjectOld(Node.Handle h, ObjectInputStream ois) 673 throws java.io.IOException , ClassNotFoundException { 674 if (h == null) { 675 return; 678 } else { 679 String [] rootCtx = (String []) ois.readObject(); 680 String [] exploredCtx = (String []) ois.readObject(); 681 List<String []> ll = new LinkedList<String []>(); 682 683 for (;;) { 684 String [] path = (String []) ois.readObject(); 685 686 if (path == null) { 687 break; 688 } 689 690 ll.add(path); 691 } 692 693 Node root = findPath(h.getNode(), rootCtx); 694 restoreSelection(root, exploredCtx, ll); 695 } 696 } 697 698 private void restoreSelection( 699 final Node root, final String [] exploredCtx, final List<?> selectedPaths) { 700 setRootContext(root); 701 702 Mutex.EVENT.readAccess( 713 new Runnable () { 714 public void run() { 715 List<Node> selNodes = new ArrayList<Node>(selectedPaths.size()); 717 718 for (Object path : selectedPaths) { 719 selNodes.add(findPath(root, (String []) path)); 720 } 721 722 Node[] newSelection = selNodes.toArray(new Node[selNodes.size()]); 724 725 if (exploredCtx != null) { 726 setExploredContext(findPath(root, exploredCtx), newSelection); 727 } else { 728 setSelectedNodes0(newSelection); 729 } 730 } 731 } 732 ); 733 } 734 735 756 public static ExplorerManager find(Component comp) { 757 for (;;) { 759 comp = comp.getParent(); 760 761 if (comp == null) { 762 return new ExplorerManager(); 764 } 765 766 if (comp instanceof Provider) { 767 return ((Provider) comp).getExplorerManager(); 769 } 770 } 771 } 772 773 774 static Node findPath(Node r, String [] path) { 775 try { 776 return NodeOp.findPath(r, path); 777 } catch (NodeNotFoundException ex) { 778 return ex.getClosestNode(); 779 } 780 } 781 782 783 static synchronized RequestProcessor getSelectionProcessor() { 784 if (selectionProcessor == null) { 785 selectionProcessor = new RequestProcessor("ExplorerManager-selection"); } 787 788 return selectionProcessor; 789 } 790 791 795 static synchronized ExplorerActionsImpl findExplorerActionsImpl(ExplorerManager em) { 796 if (em.actions == null) { 797 em.actions = new ExplorerActionsImpl(); 798 em.actions.attach(em); 799 } 800 801 return em.actions; 802 } 803 804 private void fireInAWT(final String propName, final Object oldVal, final Object newVal) { 805 if (propertySupport != null) { 806 Mutex.EVENT.readAccess( 807 new Runnable () { 808 public void run() { 809 propertySupport.firePropertyChange(propName, oldVal, newVal); 810 } 811 } 812 ); 813 } 814 } 815 816 private static String getString(String key) { 817 return NbBundle.getMessage(ExplorerManager.class, key); 818 } 819 820 824 828 public static interface Provider { 829 832 public ExplorerManager getExplorerManager(); 833 } 834 835 838 private class Listener extends NodeAdapter implements Runnable { 839 Collection<Node> removeList = new HashSet<Node>(); 840 841 Listener() { 842 } 843 844 847 public void nodeDestroyed(NodeEvent ev) { 848 if (ev.getNode().equals(getRootContext())) { 849 setRootContext(Node.EMPTY); 853 } else { 854 scheduleRemove(ev.getNode()); 856 } 857 } 858 859 862 public void propertyChange(java.beans.PropertyChangeEvent ev) { 863 fireInAWT(PROP_NODE_CHANGE, null, null); 864 } 865 866 868 private void scheduleRemove(Node n) { 869 synchronized (ExplorerManager.this) { 870 if (selectionSyncTask == null) { 871 selectionSyncTask = getSelectionProcessor().create(this); 872 } else { 873 selectionSyncTask.cancel(); 874 } 875 } 876 877 synchronized (this) { 878 removeList.add(n); 879 } 880 881 selectionSyncTask.schedule(SELECTION_SYNC_DELAY); 883 } 884 885 public void run() { 886 if (!Children.MUTEX.isReadAccess()) { 887 Children.MUTEX.readAccess(this); 888 889 return; 890 } 891 892 Collection<Node> remove; 893 894 synchronized (this) { 895 remove = removeList; 899 removeList = new HashSet<Node>(); 900 } 901 902 LinkedList<Node> newSel = new LinkedList<Node>(Arrays.asList(getSelectedNodes())); 903 Iterator<Node> it = remove.iterator(); 904 905 while (it.hasNext()) { 906 Node n_remove = it.next(); 907 908 if (newSel.contains(n_remove)) { 909 Node n_selection = newSel.get(newSel.indexOf(n_remove)); 911 912 if (!Arrays.equals(NodeOp.createPath(n_remove, null), NodeOp.createPath(n_selection, null))) { 913 it.remove(); 914 } 915 } 916 } 917 918 newSel.removeAll(remove); 919 for( Iterator<Node> i=newSel.iterator(); i.hasNext(); ) { 920 Node n = i.next(); 921 if( !isUnderRoot( n ) ) 922 i.remove(); 923 } 924 925 Node[] selNodes = newSel.toArray(new Node[newSel.size()]); 926 setSelectedNodes0(selNodes); 927 } 928 } 929 } 930 | Popular Tags |