diff --git a/openide.explorer/apichanges.xml b/openide.explorer/apichanges.xml --- a/openide.explorer/apichanges.xml +++ b/openide.explorer/apichanges.xml @@ -47,6 +47,24 @@ Explorer API + + + setSelectedNodes() does partial selection instead of throwing IllegalArgumentException when + some of newly selected nodes are not under root context. + + + + + Nodes can be removed outside of AWT thread while e.g. view can + try to select them. This generated IllegalArgumentException during checking if + such a node is under root context. Now + setSelectedNodes(). + does partial selection + of valid nodes instead of throwing IllegalArgumentException. + + + + Added OutlineView component which is a replacement for buggy TreeTableView. diff --git a/openide.explorer/manifest.mf b/openide.explorer/manifest.mf --- a/openide.explorer/manifest.mf +++ b/openide.explorer/manifest.mf @@ -1,6 +1,6 @@ Manifest-Version: 1.0 OpenIDE-Module: org.openide.explorer -OpenIDE-Module-Specification-Version: 6.15 +OpenIDE-Module-Specification-Version: 6.16 OpenIDE-Module-Implementation-Version: 1 OpenIDE-Module-Localizing-Bundle: org/openide/explorer/Bundle.properties AutoUpdate-Essential-Module: true diff --git a/openide.explorer/src/org/openide/explorer/ExplorerManager.java b/openide.explorer/src/org/openide/explorer/ExplorerManager.java --- a/openide.explorer/src/org/openide/explorer/ExplorerManager.java +++ b/openide.explorer/src/org/openide/explorer/ExplorerManager.java @@ -51,6 +51,7 @@ import java.io.*; import java.util.*; +import java.util.ArrayList; /** @@ -204,8 +205,7 @@ /** Set the set of selected nodes. * @param value the nodes to select; empty (not null) if none are to be selected * @exception PropertyVetoException when the given nodes cannot be selected - * @throws IllegalArgumentException if null is given, or if any elements - * of the selection are not within the current root context + * @throws IllegalArgumentException if null is given (array or any element in array) */ public final void setSelectedNodes(final Node[] value) throws PropertyVetoException { @@ -213,55 +213,60 @@ public PropertyVetoException veto; private boolean doFire; private Node[] oldValue; + private Node[] newValue; - /** @return false if no further processing is needed */ - private boolean checkArgumentIsValid() { + /** selects only nodes under root context */ + private void checkAndSet() { if (value == null) { throw new IllegalArgumentException(getString("EXC_NodeCannotBeNull")); } if (equalNodes(value, selectedNodes)) { - return false; + return; } + List validNodes = null; for (int i = 0; i < value.length; i++) { if (value[i] == null) { throw new IllegalArgumentException(getString("EXC_NoElementOfNodeSelectionMayBeNull")); } - // exception means sanity check failed (supplied nodes are not under root) - // the reason could be that they were deleted meanwhile, so fail gracefuly in production builds - try { - checkUnderRoot(value[i], "EXC_NodeSelectionCannotContainNodes"); - } catch (IllegalArgumentException e) { - boolean dontFailGracefully = false; - assert dontFailGracefully = true; - if (dontFailGracefully) { - throw e; - } else { - return false; + if (!isUnderRoot(value[i])) { + if (validNodes == null) { + validNodes = new ArrayList(value.length); + for (int j = 0; j < i; j++) { + validNodes.add(value[i]); + } } + } else if (validNodes != null) { + validNodes.add(value[i]); } } + if (validNodes != null) { + newValue = validNodes.toArray(new Node[validNodes.size()]); + if (equalNodes(newValue, selectedNodes)) { + return; + } + } else { + newValue = value; + } - if ((value.length != 0) && (vetoableSupport != null)) { + if ((newValue.length != 0) && (vetoableSupport != null)) { try { // we send the vetoable change event only for non-empty selections - vetoableSupport.fireVetoableChange(PROP_SELECTED_NODES, selectedNodes, value); + vetoableSupport.fireVetoableChange(PROP_SELECTED_NODES, selectedNodes, newValue); } catch (PropertyVetoException ex) { veto = ex; - - return false; + return; } } - - return true; + updateSelection(); } private void updateSelection() { oldValue = selectedNodes; addRemoveListeners(false); - selectedNodes = value; + selectedNodes = newValue; addRemoveListeners(true); doFire = true; @@ -274,9 +279,7 @@ } public void run() { - if (checkArgumentIsValid()) { - updateSelection(); - } + checkAndSet(); } } diff --git a/openide.explorer/test/unit/src/org/openide/explorer/ExplorerManagerTest.java b/openide.explorer/test/unit/src/org/openide/explorer/ExplorerManagerTest.java --- a/openide.explorer/test/unit/src/org/openide/explorer/ExplorerManagerTest.java +++ b/openide.explorer/test/unit/src/org/openide/explorer/ExplorerManagerTest.java @@ -153,13 +153,8 @@ public void testCannotSetNodesNotUnderTheRoot() throws Exception { final Node a = new AbstractNode(Children.LEAF); - - try { - em.setSelectedNodes(new Node[] { a }); - fail("Should throw IllegalArgumentException as the node is not under root"); - } catch (IllegalArgumentException ex) { - // ok, a is not under root - } + em.setSelectedNodes(new Node[]{a}); + assertEquals(0, em.getSelectedNodes().length); } diff --git a/openide.explorer/test/unit/src/org/openide/explorer/view/TreeViewTest.java b/openide.explorer/test/unit/src/org/openide/explorer/view/TreeViewTest.java --- a/openide.explorer/test/unit/src/org/openide/explorer/view/TreeViewTest.java +++ b/openide.explorer/test/unit/src/org/openide/explorer/view/TreeViewTest.java @@ -49,6 +49,7 @@ import java.lang.ref.WeakReference; import java.lang.reflect.InvocationTargetException; import java.util.Arrays; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import javax.swing.AbstractAction; import javax.swing.Action; @@ -101,7 +102,7 @@ // public static TreeViewTest suite() { // return new TreeViewTest("testSetSelectedNodeIsSynchronizedLazy"); // } - + /** * Tests whether JTree's property scrollsOnExpand * is taken into account in @@ -505,7 +506,7 @@ } fail("Node shall not be GCed: " + ref.get()); } - + public void testSetSelectedNodeIsSynchronizedEager() throws Exception { doSetSelectedNodeIsSynchronized(false); } @@ -579,6 +580,86 @@ assertEquals("Selection should be updated", Arrays.asList(keys.getNodes()), Arrays.asList(testWindow.getExplorerManager().getSelectedNodes())); if (setSel.e != null) { + fail(); + } + } + + public void testPartialNodeSelectionEager() throws Exception { + doTestPartialNodeSelection(false); + } + + public void testPartialNodeSelectionLazy() throws Exception { + doTestPartialNodeSelection(true); + } + + private void doTestPartialNodeSelection(boolean lazy) throws Exception { + assert !EventQueue.isDispatchThread(); + + testWindow = new ExplorerWindow(); + testWindow.getContentPane().add(treeView = new TestTreeView()); + + class WindowDisplayer implements Runnable { + public void run() { + testWindow.pack(); + testWindow.setVisible(true); + } + + void waitShown() throws InterruptedException { + while (!testWindow.isShowing()) { + Thread.sleep(100); + } + Thread.sleep(500); + } + } + + final Keys keys = new Keys(lazy, "A", "B", "-B", "C"); + AbstractNode node = new AbstractNode(keys); + node.setName(getName()); + final AbstractNode root = node; + + try { + WindowDisplayer wd = new WindowDisplayer(); + testWindow.getExplorerManager().setRootContext(root); + EventQueue.invokeAndWait(wd); + wd.waitShown(); + } catch (InvocationTargetException ex) { + ex.printStackTrace(); + } + + waitAWT(); + testWindow.getExplorerManager().setRootContext(root); + assertSame("Root is OK", root, testWindow.getExplorerManager().getRootContext()); + + Node[] arr = node.getChildren().getNodes(); + testWindow.getExplorerManager().setExploredContext(node); + + final Block block1 = new Block(); + final Block block2 = new Block(); + final AtomicBoolean exc = new AtomicBoolean(false); + + SwingUtilities.invokeLater(new Runnable() { + + public void run() { + Node[] arr2 = root.getChildren().getNodes(); + block1.unblock(); + block2.block(); + try { + testWindow.getExplorerManager().setSelectedNodes(arr2); + } catch (Throwable ex) { + ex.printStackTrace(); + exc.set(true); + } + } + }); + + block1.block(); + keys.keys("B", "D"); + block2.unblock(); + waitAWT(); + + assertEquals("B should be selected", Arrays.asList(keys.getNodes()[0]), + Arrays.asList(testWindow.getExplorerManager().getSelectedNodes())); + if (exc.get()) { fail(); } }