1 19 20 package org.openide.nodes; 21 22 import java.util.ArrayList ; 23 import java.util.Arrays ; 24 import java.util.Collection ; 25 import java.util.Collections ; 26 import org.netbeans.junit.NbTestCase; 27 import org.openide.cookies.OpenCookie; 28 import org.openide.util.Lookup; 29 import org.openide.util.RequestProcessor; 30 import org.openide.util.actions.SystemAction; 31 import org.openide.util.lookup.AbstractLookup; 32 import org.openide.util.lookup.InstanceContent; 33 34 38 public class FilterNodeTest extends NbTestCase { 39 40 public FilterNodeTest(String name) { 41 super(name); 42 } 43 44 46 public void testChangeOriginalLeafToArray () { 47 AbstractNode a = new AbstractNode (Children.LEAF); 48 AbstractNode b = new AbstractNode (Children.LEAF); 49 AbstractNode c = new AbstractNode (new Children.Array ()); 50 51 52 FN fn = new FN (a); 53 fn.changeCh (b, true); 54 fn.changeCh (c, true); 55 } 56 57 public void testYouCannotBeYourOwnOriginal () { 58 doOriginalsMayNotFormCycle (0); 59 } 60 public void testYouCannotBeYourOwnOriginal1 () { 61 doOriginalsMayNotFormCycle (1); 62 } 63 public void testYouCannotBeYourOwnOriginal2 () { 64 doOriginalsMayNotFormCycle (2); 65 } 66 public void testYouCannotBeYourOwnOriginal3 () { 67 doOriginalsMayNotFormCycle (3); 68 } 69 70 private void doOriginalsMayNotFormCycle (int length) { 71 FilterNode first = new FilterNode (Node.EMPTY); 72 FilterNode node = first; 73 74 for (int i = 0; i < length; i++) { 75 node = new FilterNode (node); 76 } 77 78 try { 79 first.changeOriginal (node, false); 81 node.hashCode (); 83 node.getName (); 84 } catch (java.lang.IllegalArgumentException ex) { 85 } 87 } 88 89 public void testHashCodeStackOverflowDetectionToFindOutTheProblemOfIssue46993 () { 90 FilterNode node = new FilterNode (Node.EMPTY, null, org.openide.util.Lookup.EMPTY); 91 FilterNode n2 = new FilterNode (node); 92 FilterNode n3 = new FilterNode (n2); 93 94 95 96 class FireL extends NodeAdapter { 97 private boolean thrown; 98 public void propertyChange (java.beans.PropertyChangeEvent ev) { 99 thrown = true; 100 throw new IllegalStateException ("my"); 101 } 102 } 103 FireL f = new FireL (); 104 n3.addNodeListener (f); 106 n3.removeNodeListener (f); 107 node.addNodeListener (f); 108 109 110 try { 111 node.changeOriginal (n3, false); 112 } catch (IllegalStateException ex) { 113 assertTrue ("Really thrown ex from my listener", f.thrown); 114 assertEquals ("my", ex.getMessage ()); 115 } catch (IllegalArgumentException ex) { 116 return; 121 } 122 123 try { 124 n3.hashCode (); 125 fail ("This has to throw an error"); 126 } catch (Error err) { 127 if (! (err instanceof StackOverflowError )) { 128 fail ("Wrong class: " + err.getClass ()); 129 } 130 assertIdentityHashCode (err.getMessage (), node); 131 assertIdentityHashCode (err.getMessage (), n2); 132 assertIdentityHashCode (err.getMessage (), n3); 133 } 134 } 135 136 private static void assertIdentityHashCode (String msg, Object obj) { 137 String hex = Integer.toString (System.identityHashCode (obj), 16); 138 int indx = msg != null ? msg.indexOf (hex) : -1; 139 if (indx == -1) { 140 fail ("Message <" + msg + "> should contain identityHashCode of " + obj + " which is " + hex + "\nAre you sure you are running tests with assertions enabled!?"); 141 } 142 } 143 144 145 147 public void testChangeOriginalArrayToLeaf () { 148 AbstractNode a = new AbstractNode (Children.LEAF); 149 AbstractNode b = new AbstractNode (new Children.Array ()); 150 AbstractNode c = new AbstractNode (new Children.Array ()); 151 152 153 FN fn = new FN (c); 154 fn.changeCh (b, true); 155 fn.changeCh (a, true); 156 } 157 158 159 public void testEquals() { 160 Node original = new AbstractNode(Children.LEAF); 161 FilterNode filter = new FilterNode(original); 162 163 assertTrue("Equals of filter node and its original is not symmetric", 165 (filter.equals(original)) == (original.equals(filter))); 166 } 167 168 public void testHashCodeAndEquals () { 169 Node original = new AbstractNode(Children.LEAF); 170 FilterNode filter = new FilterNode(original); 171 172 assertTrue ("They are equal", filter.equals (original)); 173 assertTrue ("In both directions", original.equals (filter)); 174 assertEquals ("Have the same hashcode", original.hashCode(), filter.hashCode ()); 175 } 176 177 179 public void testGetActions () { 180 final ArrayList contextActions = new ArrayList (); 181 final ArrayList actions = new ArrayList (); 182 183 class AA extends javax.swing.AbstractAction { 184 public void actionPerformed (java.awt.event.ActionEvent ev) { 185 } 186 } 187 188 contextActions.add (new AA ()); 189 contextActions.add (null); 190 contextActions.add (new AA ()); 191 192 actions.add (new AA ()); 193 actions.add (new AA ()); 194 195 final javax.swing.Action pref = new AA (); 196 197 AbstractNode n = new AbstractNode (Children.LEAF) { 198 public javax.swing.Action [] getActions (boolean context) { 199 ArrayList l = context ? contextActions : actions; 200 return (javax.swing.Action [])l.toArray ( 201 new javax.swing.Action [0] 202 ); 203 } 204 205 public javax.swing.Action getPreferredAction () { 206 return pref; 207 } 208 }; 209 210 FilterNode fn = new FilterNode (n); 211 212 213 assertEquals ("Same context actions", contextActions, Arrays.asList (fn.getActions(true))); 214 assertEquals ("Same actions", actions, Arrays.asList (fn.getActions(false))); 215 assertEquals ("Same preffered action", pref, fn.getPreferredAction()); 216 217 218 fn = new FilterNode (n) { 219 public SystemAction getDefaultAction () { 220 return SystemAction.get (OpenAction.class); 221 } 222 223 public SystemAction[] getActions () { 224 return new SystemAction[] { getDefaultAction () }; 225 } 226 }; 227 228 assertEquals ("Overriding getDefaultAction wins", fn.getDefaultAction (), fn.getPreferredAction()); 229 assertEquals ("Overriding getActions wins", Arrays.asList (fn.getActions()), Arrays.asList (fn.getActions(false))); 230 assertEquals ("Same context actions", contextActions, Arrays.asList (fn.getActions(true))); 231 232 233 fn = new FilterNode (n) { 234 public SystemAction getDefaultAction () { 235 return SystemAction.get (OpenAction.class); 236 } 237 238 public SystemAction[] getContextActions () { 239 return new SystemAction[] { getDefaultAction () }; 240 } 241 }; 242 243 assertEquals ("Overriding getDefaultAction wins", fn.getDefaultAction (), fn.getPreferredAction()); 244 assertEquals ("Same actions", actions, Arrays.asList (fn.getActions(false))); 245 assertEquals ("Overriding getContextActions wins", Arrays.asList (fn.getContextActions()), Arrays.asList (fn.getActions (true))); 246 } 247 248 public void testUpdateLeaf () { 249 AbstractNode a = new AbstractNode (Children.LEAF); 250 FilterNode fn = new FilterNode (a); 251 assertEquals ("Children is leaf", Children.LEAF, fn.getChildren ()); 252 253 a.setChildren(new Children.Array ()); 254 assertFalse("Children of FilterNode not updated", fn.isLeaf()); 255 assertTrue ("Children are not leaf", fn.getChildren () != Children.LEAF); 256 257 class Counter extends NodeAdapter { 258 public int cnt; 259 260 public void propertyChange (java.beans.PropertyChangeEvent ev) { 261 if (Node.PROP_LEAF.equals (ev.getPropertyName())) { 262 cnt++; 263 } 264 } 265 } 266 267 Counter counter = new Counter (); 268 269 fn.addNodeListener(counter); 270 a.setChildren (Children.LEAF); 271 272 assertEquals ("Children is leaf", Children.LEAF, fn.getChildren ()); 273 assertTrue ("Now it is LEAF again", fn.isLeaf ()); 274 assertEquals ("One change", 1, counter.cnt); 275 276 a.setChildren (new Children.Array ()); 277 assertFalse ("Again has children", fn.isLeaf ()); 278 assertEquals ("Another change", 2, counter.cnt); 279 280 } 281 282 283 public void testUpdateLeafWithProvidedChildren () { 284 AbstractNode node = new AbstractNode (Children.LEAF); 285 FilterNode fn = new FilterNode (node, new Children.Array ()); 286 287 assertFalse ("filter node is not leaf, it has Array children", fn.isLeaf ()); 288 289 290 node = new AbstractNode (new Children.Array ()); 291 fn = new FilterNode (node, Children.LEAF); 292 293 assertTrue ("filter node is leaf as children were provided", fn.isLeaf ()); 294 } 295 296 public void testIsLeafCanBeCalledWhenAnotherThreadHoldsALock () throws Exception { 297 final FilterNode fn = new FilterNode (Node.EMPTY); 298 final RequestProcessor rp = new RequestProcessor ("Will deadlock"); 299 300 assertTrue ("Is leaf", fn.isLeaf ()); 301 302 class BlockInReadAccess implements Runnable { 303 int cnt; 304 305 public synchronized void run () { 306 if (cnt++ == 0) { 307 Children.MUTEX.readAccess(this); 308 return; 309 } 310 311 try { 312 notify (); 313 wait (); 314 } catch (InterruptedException ex) { 315 ex.printStackTrace(); 316 } 317 cnt = -1; 318 notify (); 319 } 320 } 321 322 BlockInReadAccess b = new BlockInReadAccess (); 323 synchronized (b) { 324 rp.post (b); 325 b.wait (); 326 } 328 329 assertTrue ("Is leaf can be called", fn.isLeaf ()); 330 331 synchronized (b) { 332 b.notify (); 333 b.wait (); 334 } 335 336 assertEquals ("B finished", -1, b.cnt); 337 } 338 339 public void testIsLeafDoesNotChangeWhileInReadAccess() throws Exception { 340 AbstractNode a = new AbstractNode (Children.LEAF); 341 final FilterNode fn = new FilterNode (a); 342 343 assertTrue ("Is leaf", fn.isLeaf ()); 344 345 a.setChildren(new Children.Array ()); 347 348 class ReadAccess implements Runnable { 349 public void run () { 350 assertTrue ( 351 "It still claims that it is leaf because it cannot call setChildren to update, " + 352 " because that would upgrade the lock from read to write and that is not allowed", fn.isLeaf ()); 353 } 354 } 355 Children.MUTEX.readAccess (new ReadAccess ()); 356 357 358 assertFalse ("But as soon as the read access ends it is updated", fn.isLeaf ()); 359 } 360 361 public void testGetSetValue() { 362 AbstractNode node = new AbstractNode (Children.LEAF); 363 364 FN fn_no = new FN (node); 365 fn_no.disableDel(FN.DELEGATE_SET_VALUE | FN.DELEGATE_GET_VALUE); 366 367 FN fn_get = new FN (node); 368 fn_get.disableDel(FN.DELEGATE_SET_VALUE); 369 370 FN fn_set = new FN (node); 371 fn_set.disableDel(FN.DELEGATE_GET_VALUE); 372 373 FilterNode fn_both = new FilterNode (node); 374 375 node.setValue("val1", "item1"); 376 assertTrue("Should not delegate getValue", fn_no.getValue("val1") == null); 377 assertEquals("Should delegate getValue", "item1", fn_get.getValue("val1")); 378 assertTrue("Should not delegate getValue", fn_set.getValue("val1") == null); 379 assertEquals("Should delegate getValue", "item1", fn_both.getValue("val1")); 380 381 fn_no.setValue("val1", "xxx"); 382 assertEquals("Should have the value", "xxx", fn_no.getValue("val1")); 383 assertEquals("Should not propagate setValue", "item1", node.getValue("val1")); 384 385 fn_get.setValue("val1", "xxx"); 386 assertEquals("Should still detegate getValue", "item1", fn_get.getValue("val1")); 387 assertEquals("Should not propagate setValue", "item1", node.getValue("val1")); 388 389 fn_set.setValue("val1", "item2"); 390 assertTrue("Should not delegate getValue", fn_set.getValue("val1") == null); 391 assertEquals("Should propagate setValue", "item2", node.getValue("val1")); 392 393 fn_both.setValue("val1", "item3"); 394 assertTrue("Should still detegate getValue", fn_both.getValue("val3") == null); 395 assertEquals("Should propagate setValue", "item3", node.getValue("val1")); 396 } 397 398 public void testChildrenFireCorrectEvents () throws Exception { 399 doChildrenFireCorrectEvents (false); 400 } 401 402 public void testSubclassedChildrenFireCorrectEvents () throws Exception { 403 doChildrenFireCorrectEvents (true); 404 } 405 406 private void doChildrenFireCorrectEvents (boolean subclassedChildren) throws Exception { 407 ChildrenKeysTest.Keys k = new ChildrenKeysTest.Keys (new String [] { "1", "2", "3" }); 408 AbstractNode an = new AbstractNode (k); 409 410 FilterNode fn; 411 if (subclassedChildren) { 412 class Sub extends FilterNode.Children { 413 public Sub (Node n) { 414 super (n); 415 } 416 } 417 fn = new FilterNode (an, new Sub (an)); 418 } else { 419 fn = new FilterNode (an); 420 } 421 ChildrenKeysTest.Listener l = new ChildrenKeysTest.Listener (); 422 fn.addNodeListener (l); 423 424 assertEquals ("Three", 3, fn.getChildren ().getNodesCount ()); 425 426 l.assertNoEvents ("Well, we are asking for the first time"); 427 428 Node n1, n2; 429 n1 = fn.getChildren ().getNodeAt (0); 430 n2 = fn.getChildren ().getNodeAt (2); 431 assertEquals ("Name is 1", "1", n1.getName ()); 432 assertEquals ("Name is 3", "3", n2.getName ()); 433 434 l.assertNoEvents ("No changes that would be observable from outside"); 435 436 k.keys (new String [] { "1", "3"}); 437 438 NodeMemberEvent ev = l.assertEvents (1); 439 assertEquals ("Removal event type", NodeMemberEvent.class, ev.getClass ()); 440 assertFalse ("It is removal", ev.isAddEvent ()); 441 int[] removed = ev.getDeltaIndices (); 442 assertEquals ("One node gone", 1, removed.length); 443 assertEquals ("Middle one", 1, removed[0]); 444 } 445 446 public void testFilterNodeCanGCNodes () { 447 class K extends Children.Keys { 448 public int addNotify; 449 public int removeNotify; 450 public int optimal; 451 public int nonoptimal; 452 public java.lang.ref.Reference keyRef; 453 454 protected void addNotify () { 455 addNotify++; 456 457 Integer key = new Integer (50); 458 setKeys (Collections.singleton (key)); 459 keyRef = new java.lang.ref.WeakReference (key); 460 } 461 462 protected void removeNotify () { 463 removeNotify++; 464 setKeys (Collections.EMPTY_LIST); 465 } 466 467 468 public Node[] getNodes (boolean optimal) { 469 if (optimal) { 470 assertEquals ("No addNotify yet", 0, addNotify); 471 this.optimal++; 472 } else { 473 this.nonoptimal++; 474 } 475 Node[] ret = super.getNodes (); 476 assertEquals ("addNotify done", 1, addNotify); 477 return ret; 478 } 479 480 protected Node[] createNodes (Object obj) { 481 return new Node[] { Node.EMPTY.cloneNode () }; 482 } 483 } 484 K k = new K (); 485 AbstractNode n = new AbstractNode (k); 486 FilterNode fn = new FilterNode (n); 487 488 Node[] arr = fn.getChildren ().getNodes (true); 489 assertEquals ("Add notify called", 1, k.addNotify); 490 assertEquals ("optimal called", 1, k.optimal); 491 assertEquals ("nonoptimal not called", 0, k.nonoptimal); 492 assertEquals ("One node", 1, arr.length); 493 494 java.lang.ref.WeakReference ref = new java.lang.ref.WeakReference (arr[0]); 495 assertEquals ("No removeNotify", 0, k.removeNotify); 496 arr = null; 497 assertGC ("The node can go away", ref); 498 assertGC ("Key can go away", k.keyRef); 499 assertEquals ("One remove notify", 1, k.removeNotify); 500 501 arr = fn.getChildren ().getNodes (); 502 assertEquals ("Add notify called once more", 2, k.addNotify); 503 assertEquals ("optimal stays as it was", 1, k.optimal); 504 assertEquals ("nonoptimal stays", 0, k.nonoptimal); 505 assertEquals ("still one remove", 1, k.removeNotify); 506 assertEquals ("nonoptimal not called", 0, k.nonoptimal); 507 assertEquals ("One node", 1, arr.length); 508 509 k.setKeys (new Object [] { new Integer (10), new Integer (50), new Integer (70) }); 510 511 arr = fn.getChildren ().getNodes (); 512 513 assertEquals ("Three", 3, arr.length); 514 assertEquals ("Add notify called once more", 2, k.addNotify); 515 assertEquals ("optimal stays as it was", 1, k.optimal); 516 assertEquals ("nonoptimal stays", 0, k.nonoptimal); 517 assertEquals ("still one remove", 1, k.removeNotify); 518 assertEquals ("nonoptimal not called", 0, k.nonoptimal); 519 } 520 521 public void testChangesInKeysPropagatedCorrectlyIntoFilterNodeKeys () { 522 class K extends Children.Keys { 523 public int addNotify; 524 public int removeNotify; 525 public int optimal; 526 527 protected void addNotify () { 528 addNotify++; 529 } 530 531 protected void removeNotify () { 532 removeNotify++; 533 setKeys (Collections.EMPTY_LIST); 534 } 535 536 537 public Node[] getNodes (boolean optimal) { 538 if (optimal) { 539 Integer key = new Integer (50); 540 setKeys (Collections.singleton (key)); 541 this.optimal++; 542 } 543 Node[] ret = super.getNodes (); 544 return ret; 545 } 546 547 protected Node[] createNodes (Object obj) { 548 return new Node[] { Node.EMPTY.cloneNode () }; 549 } 550 } 551 K k = new K (); 552 AbstractNode n = new AbstractNode (k); 553 FilterNode fn = new FilterNode (n); 554 555 Node[] arr = fn.getChildren ().getNodes (); 556 assertEquals ("No nodes", 0, arr.length); 557 558 arr = fn.getChildren ().getNodes (true); 559 assertEquals ("Add notify called", 1, k.addNotify); 560 assertEquals ("optimal called", 1, k.optimal); 561 assertEquals ("One node", 1, arr.length); 562 563 k.setKeys (new Object [] { new Integer (1), new Integer (50) }); 564 565 arr = fn.getChildren ().getNodes (); 566 assertEquals ("Two nodes", 2, arr.length); 567 568 } 569 570 public void testFilterNodeChildrenThatOnceReturnedNullAreThenEmptyForTheRestOfNodesBug () { 571 doFilterNodeChildrenThatOnceReturnedNullAreThenEmptyForTheRestOfNodesBug (false); 572 } 573 574 public void testFilterNodeChildrenThatOnceReturnedNullAreThenEmptyForTheRestOfNodesBugEvenWithBefore () { 575 doFilterNodeChildrenThatOnceReturnedNullAreThenEmptyForTheRestOfNodesBug (true); 576 } 577 578 private void doFilterNodeChildrenThatOnceReturnedNullAreThenEmptyForTheRestOfNodesBug (boolean before) { 579 final Children.Array ch = new Children.Array (); 580 ch.add (new Node[] { 581 createNode ("1"), 582 createNode ("2"), createNode ("3"), 584 }); 585 final AbstractNode an = new AbstractNode (ch); 586 587 588 class K extends FilterNode.Children { 589 public boolean nodeFound; 590 591 public K () { 592 super (an); 593 } 594 595 @Override 596 protected Node[] createNodes(Node o) { 597 Node n = ch.getNodes()[1]; 598 if (o == n) { 599 nodeFound = true; 600 return null; 601 } 602 return super.createNodes (o); 603 } 604 } 605 K k = new K (); 606 k.setBefore (before); 607 FilterNode fn = new FilterNode (an, k); 608 609 Node[] arr = fn.getChildren ().getNodes (); 610 assertTrue ("The createNodes method was called for the right node", k.nodeFound); 611 assertEquals ("There are two nodes", 2, arr.length); 612 613 } 614 615 private static AbstractNode createNode (String name) { 616 AbstractNode an = new AbstractNode (Children.LEAF); 617 an.setName (name); 618 return an; 619 } 620 621 622 624 private static final class FN extends FilterNode { 625 public FN (Node orig) { 626 super (orig); 627 } 628 629 public void changeCh (Node n, boolean children) { 630 changeOriginal (n, children); 631 } 632 633 public void disableDel (int mask) { 634 disableDelegation(mask); 635 } 636 637 } 638 639 public void testLookupNode() { 640 class NodeA extends AbstractNode { 641 public NodeA() { 642 super(Children.LEAF); 643 } 644 } 645 646 class NodeB extends AbstractNode { 647 public NodeB() { 648 this(Children.LEAF, new InstanceContent()); 649 } 650 651 NodeB(Children ch, InstanceContent ic) { 652 super(ch, new AbstractLookup(ic)); 653 ic.add(this); 654 } 655 } 656 657 FilterNode n = new FilterNode(new NodeB()); 658 Object o = n.getLookup().lookup(NodeA.class); 659 assertNull("There is no instance of NodeA in the lookup, we should get null here:" + o, o); 660 661 Lookup.Item item = n.getLookup().lookupItem(new Lookup.Template(NodeA.class)); 662 assertNull("There is no instance of NodeA in the lookup, there shall be no item:" + item, item); 663 664 Lookup.Result res = n.getLookup().lookupResult(NodeA.class); 665 Collection c; 666 c = res.allClasses(); 667 assertTrue("No classes:" + c, c.isEmpty()); 668 c = res.allItems(); 669 assertTrue("No items:" + c, c.isEmpty()); 670 c = res.allInstances(); 671 assertTrue("No instances:" + c, c.isEmpty()); 672 } 673 674 public void testNoClassCast89329() throws Exception { 675 InstanceContent ic = new InstanceContent(); 676 AbstractLookup lookup = new AbstractLookup(ic); 677 AbstractNode a = new AbstractNode(Children.LEAF, lookup); 678 FilterNode f = new FilterNode(a); 679 680 ic.add("Kuk"); 681 682 Class what = String .class; 683 assertNull("Indeed null, string is not a cookie", f.getCookie(what)); 684 assertEquals("Kuk", f.getLookup().lookup(String .class)); 685 } 686 public void testNoClass2Cast89329() throws Exception { 687 InstanceContent ic = new InstanceContent(); 688 AbstractLookup lookup = new AbstractLookup(ic); 689 AbstractNode a = new AbstractNode(Children.LEAF, lookup); 690 691 class F extends FilterNode implements OpenCookie { 692 public F(Node n) { 693 super(n); 694 } 695 public Node.Cookie getCookie(Class type) { 696 if (OpenCookie.class.isAssignableFrom(type)) return this; 697 else return super.getCookie(type); 698 } 699 700 public void open() { 701 } 702 } 703 704 FilterNode f = new F(a); 705 706 ic.add("Kuk"); 707 708 Class what = String .class; 709 assertNull("Indeed null, string is not a cookie", f.getCookie(what)); 710 assertEquals("Kuk", f.getLookup().lookup(String .class)); 711 } 712 } 713 714 | Popular Tags |