blob: ad6cc8e96c360d1e231576bce3a7875f0499e781 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.spi.actions;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.swing.Action;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.netbeans.junit.MockServices;
import org.netbeans.junit.NbTestCase;
import org.openide.util.ContextAwareAction;
import org.openide.util.lookup.AbstractLookup;
import org.openide.util.lookup.InstanceContent;
import org.openide.util.ContextGlobalProvider;
import org.openide.util.Lookup;
import org.openide.util.Utilities;
import org.openide.util.lookup.Lookups;
import static org.junit.Assert.*;
/**
*
* @author Tim Boudreau
*/
public class ContextActionTest extends NbTestCase {
public ContextActionTest(String x) {
super(x);
}
@BeforeClass
public static void setUpClass() throws Exception {
}
@AfterClass
public static void tearDownClass() throws Exception {
}
private InstanceContent content;
private Lookup lkp;
//This timeout is just to make sure if a test fails, the test suite
//exits eventually. If some tests fail on slow machines, try increasing
//it.
private static final int TIMEOUT = 100;
@Before
@Override
public void setUp() {
//This will cause ContextAction instances to invoke notifyAll()
//when their enablement changes
ContextAction.unitTest = true;
MockServices.setServices(Provider.class);
ContextGlobalProvider x = Lookup.getDefault().lookup(ContextGlobalProvider.class);
assertNotNull(x);
assertTrue(x instanceof Provider);
Provider p = (Provider) x;
content = p.content;
lkp = p.lkp;
//some sanity checks
setContent("hello");
assertEquals("hello", lkp.lookupAll(String.class).iterator().next());
assertEquals("hello", Utilities.actionsGlobalContext().lookupAll(String.class).iterator().next());
clearContent();
assertEquals(null, lkp.lookup(String.class));
assertEquals(null, Utilities.actionsGlobalContext().lookup(String.class));
assertEquals(0, Utilities.actionsGlobalContext().lookupAll(Object.class).size());
}
static Provider instance;
public static class Provider implements ContextGlobalProvider {
private final Lookup lkp;
private final InstanceContent content;
public Provider() {
lkp = new AbstractLookup(content = new InstanceContent());
instance = this;
}
public Lookup createGlobalContext() {
return lkp;
}
}
void setContent(Object... stuff) {
content.set(Arrays.asList(stuff), null);
}
void clearContent() {
content.set(Collections.EMPTY_SET, null);
}
public void testEnablement() {
System.out.println("testEnablement");
assertEquals(0, lkp.lookupAll(String.class).size()); //sanity check
A a = new A();
assertFalse(a.isEnabled());
setContent("testEnablement");
assertEquals(1, lkp.lookupAll(String.class).size()); //sanity check
assertTrue(a.isEnabled());
clearContent();
assertFalse(a.isEnabled());
}
public void testEnablementFired() throws Exception {
System.out.println("testEnablementFired");
A a = new A();
assertEquals(0, lkp.lookupAll(String.class).size());
PCL pcl = new PCL();
a.addPropertyChangeListener(pcl);
assertFalse(a.isEnabled());
setContent("testEnablementFired");
synchronized (a) {
a.wait(TIMEOUT);
}
synchronized (pcl) {
pcl.wait(TIMEOUT);
}
assertTrue(a.isEnabled());
pcl.assertEnabledChangedTo(true);
clearContent();
assertFalse(a.isEnabled());
synchronized (a) {
a.wait(TIMEOUT);
}
synchronized (pcl) {
pcl.wait(TIMEOUT);
}
pcl.assertEnabledChangedTo(false);
setContent("woo");
synchronized (a) {
a.wait(TIMEOUT);
}
synchronized (pcl) {
pcl.wait(TIMEOUT);
}
pcl.assertEnabledChangedTo(true);
setContent("hello", "goodbye");
synchronized (a) {
a.wait(TIMEOUT);
}
synchronized (pcl) {
pcl.wait(TIMEOUT);
}
pcl.assertEnabledChangedTo(false);
assertFalse(a.isEnabled());
clearContent();
synchronized (a) {
a.wait(TIMEOUT);
}
synchronized (pcl) {
pcl.wait(TIMEOUT);
}
pcl.assertNotFired();
a.removePropertyChangeListener(pcl);
setContent("hmm");
assertTrue(a.isEnabled());
pcl.assertNotFired();
}
public void testContextInstancesDoNotInterfereWithEachOtherOrParent() throws Exception {
System.out.println("testContextInstancesDoNotInterfereWithEachOtherOrParent");
A a = new A();
assertNull(Utilities.actionsGlobalContext().lookup(String.class)); //sanity check
assertEquals("A", a.getValue(Action.NAME));
Action a1 = a.createContextAwareInstance(Lookup.EMPTY);
assertFalse(a.isEnabled());
assertEquals("A", a1.getValue(Action.NAME));
Action a2 = a.createContextAwareInstance(Lookups.fixed("testGeneralBehavior"));
assertTrue(a2.isEnabled());
assertFalse(a.isEnabled());
setContent("foo");
assertTrue(a.isEnabled());
assertFalse(a1.isEnabled());
assertTrue(a2.isEnabled());
clearContent();
assertFalse(a.isEnabled());
assertTrue(a2.isEnabled());
}
public void testContextInstancesAreIndependent() throws Exception {
System.out.println("testContextInstancesAreIndependent");
A a = new A();
assertNull(Utilities.actionsGlobalContext().lookup(String.class)); //sanity check
InstanceContent ic = new InstanceContent();
Lookup l = new AbstractLookup(ic);
Action a3 = a.createContextAwareInstance(l);
assertFalse(a3.isEnabled());
PCL pcl = new PCL();
a3.addPropertyChangeListener(pcl);
setContent("fuddle");
a.assertNotPerformed();
assertTrue(a.isEnabled());
assertFalse(a3.isEnabled());
synchronized (a3) {
//should time out if test is going to pass
a3.wait(TIMEOUT);
}
synchronized (pcl) {
pcl.wait(TIMEOUT);
}
pcl.assertNotFired();
ic.set(Collections.singleton("boo"), null);
synchronized (a3) {
a3.wait(TIMEOUT);
}
synchronized (pcl) {
pcl.wait(TIMEOUT);
}
pcl.assertEnabledChangedTo(true);
clearContent();
assertTrue(a3.isEnabled());
assertFalse(a.isEnabled());
}
public void testGetSetValue() {
System.out.println("testGetSetValue");
A a = new A();
Action a1 = a.createContextAwareInstance(Lookup.EMPTY);
Action a2 = a.createContextAwareInstance(Lookups.fixed("testGeneralBehavior"));
a.putValue("foo", "bar");
assertEquals("bar", a.getValue("foo"));
assertEquals("bar", a1.getValue("foo"));
assertEquals("bar", a2.getValue("foo"));
a1.putValue("x", "y");
assertNull(a.getValue("x"));
assertEquals("y", a1.getValue("x"));
a.putValue("x", "z");
assertEquals("y", a1.getValue("x"));
assertEquals("z", a.getValue("x"));
}
public void testSingleNotEnabledOnMoreThanOne() throws Exception {
System.out.println("testSingleNotEnabledOnMoreThanOne");
A a = new A();
assertFalse(a.createContextAwareInstance(Lookups.fixed("moo", "goo")).isEnabled());
assertTrue(a.createContextAwareInstance(Lookups.fixed("foo")).isEnabled());
}
public void testExactCount() throws Exception {
System.out.println("testExactCount");
C c = new C();
assertFalse(c.isEnabled());
setContent("1");
assertFalse(c.isEnabled());
setContent("1", "2", "3", "4", "5");
assertTrue(c.isEnabled());
clearContent();
assertFalse(c.isEnabled());
setContent("1", "2", "3", "4", "5");
assertTrue(c.isEnabled());
setContent("1", "2", "3", "4", "5", "6");
assertFalse(c.isEnabled());
}
public void testNesting() throws Exception {
System.out.println("testNesting");
D d = new D();
Thing thingOne = new Thing("a");
Thing thingTwo = new Thing("b");
Thing thingThree = new Thing("c");
Thing thingFour = new Thing("d");
Thing badThing = new Thing("no");
FakeNode node1 = new FakeNode(thingOne);
FakeNode node2 = new FakeNode(thingTwo);
FakeNode node3 = new FakeNode(thingThree);
FakeNode node4 = new FakeNode(thingFour);
FakeNode nodeBad = new FakeNode(badThing);
PCL pcl = new PCL();
d.addPropertyChangeListener(pcl);
assertFalse(d.isEnabled());
setContent(node1);
assertTrue(d.isEnabled());
synchronized (d) {
d.wait(TIMEOUT);
}
synchronized (pcl) {
pcl.wait(TIMEOUT);
}
pcl.assertEnabledChangedTo(true);
clearContent();
synchronized (d) {
d.wait(TIMEOUT);
}
synchronized (pcl) {
pcl.wait(TIMEOUT);
}
pcl.assertEnabledChangedTo(false);
assertFalse(d.isEnabled());
setContent(node1, node2, node3, node4);
assertTrue(d.isEnabled());
setContent(node1, node2, node3, node4, nodeBad);
synchronized (d) {
d.wait(TIMEOUT);
}
synchronized (pcl) {
pcl.wait(TIMEOUT);
}
pcl.assertEnabledChangedTo(false);
assertFalse(d.isEnabled());
setContent(node1, node2, node3, node4);
assertTrue(d.isEnabled());
d.actionPerformed((ActionEvent) null);
Set<Thing> expected = new HashSet<Thing>(Arrays.asList(
thingOne, thingTwo, thingThree, thingFour));
d.assertTargets(expected);
clearContent();
assertFalse(d.isEnabled());
FakeNode doesntHaveAThing = new FakeNode("Hello");
d = new D(true);
assertFalse(d.isEnabled());
setContent(node1);
assertTrue(d.isEnabled());
clearContent();
assertFalse(d.isEnabled());
setContent(node1, node2, node3, node4);
assertTrue(d.isEnabled());
clearContent();
assertFalse(d.isEnabled());
setContent(node1, node2, node3, doesntHaveAThing);
assertFalse(d.isEnabled());
}
public void testDeepDelegation() throws Exception {
System.out.println("testDeepDelegation");
E e = new E();
IndirectAction a = new IndirectAction(FakeNode.class,
new IndirectAction(FakeProject.class, e, false), false);
Thing thing1 = new Thing("a");
Thing thing2 = new Thing("b");
Thing thing3 = new Thing("c");
Thing thing4 = new Thing("d");
Thing badThing = new Thing("no");
FakeProject proj1 = new FakeProject(thing1);
FakeProject proj2 = new FakeProject(thing2);
FakeProject proj3 = new FakeProject(thing3);
FakeProject proj4 = new FakeProject(thing4);
FakeProject projBad = new FakeProject(badThing);
FakeNode node1 = new FakeNode(proj1);
FakeNode node2 = new FakeNode(proj2);
FakeNode node3 = new FakeNode(proj3);
FakeNode node4 = new FakeNode(proj4);
FakeNode nodeBad = new FakeNode(projBad);
assertFalse(a.isEnabled());
setContent(node1, node2);
assertTrue(a.isEnabled());
setContent(node1, node2, node3);
assertTrue(a.isEnabled());
clearContent();
assertFalse(a.isEnabled());
setContent(node1, node2, node4);
assertTrue(a.isEnabled());
setContent(node1, node2, nodeBad);
assertFalse(a.isEnabled());
setContent(node1, node2);
a.actionPerformed((ActionEvent) null);
e.assertTargets(new HashSet<Thing>(Arrays.asList(thing1, thing2)));
clearContent();
PCL pcl = new PCL();
a.addPropertyChangeListener(pcl);
setContent(node1, node3);
synchronized (a) {
a.wait(TIMEOUT);
}
synchronized (pcl) {
pcl.wait(TIMEOUT);
}
pcl.assertEnabledChangedTo(true);
clearContent();
synchronized (a) {
a.wait(TIMEOUT);
}
synchronized (pcl) {
pcl.wait(TIMEOUT);
}
pcl.assertEnabledChangedTo(false);
setContent(node1, node3);
synchronized (a) {
a.wait(TIMEOUT);
}
synchronized (pcl) {
pcl.wait(TIMEOUT);
}
pcl.assertEnabledChangedTo(true);
setContent(nodeBad);
synchronized (a) {
a.wait(TIMEOUT);
}
synchronized (pcl) {
pcl.wait(TIMEOUT);
}
pcl.assertEnabledChangedTo(false);
}
public void testIndirectionWithSomeNonMatches() throws Exception {
System.out.println("testIndirectionWithSomeNonMatches");
E e = new E();
IndirectAction a = new IndirectAction(FakeNode.class,
new IndirectAction(FakeProject.class, e, true), true);
Thing thing1 = new Thing("a");
Thing thing2 = new Thing("b");
FakeProject proj1 = new FakeProject(thing1);
FakeProject proj2 = new FakeProject(thing2);
FakeProject projNonMatch = new FakeProject("foo");
FakeNode node1 = new FakeNode(proj1);
FakeNode node2 = new FakeNode(proj2);
FakeNode nodeNonMatch = new FakeNode("bar");
FakeNode nodeWithProjNonMatch = new FakeNode(projNonMatch);
setContent(node1, node2);
assertTrue(a.isEnabled());
clearContent();
assertFalse(a.isEnabled());
setContent(node1, node2);
setContent(nodeNonMatch);
assertFalse(a.isEnabled());
setContent(nodeNonMatch, node1);
assertFalse(a.isEnabled());
setContent(node1);
assertTrue(a.isEnabled());
setContent(node1, nodeWithProjNonMatch);
assertFalse(a.isEnabled());
setContent(nodeWithProjNonMatch);
assertFalse(a.isEnabled());
}
public void testMerge() throws Exception {
System.out.println("testMerge");
E looksInProject = new E();
IndirectAction a =
new IndirectAction(FakeProject.class, looksInProject, true);
E looksInGlobalSelection = new E();
looksInGlobalSelection.putValue(Action.NAME, "Global");
looksInProject.putValue(Action.NAME, "DataObject");
ContextAwareAction merge = ContextAction.merge(looksInGlobalSelection, a);
// merge.addPropertyChangeListener(new PCL());
Thing thing = new Thing("thing");
FakeProject project = new FakeProject(thing);
assertFalse(merge.isEnabled());
assertFalse(looksInGlobalSelection.isEnabled());
assertFalse(looksInProject.isEnabled());
setContent(thing);
assertTrue(looksInGlobalSelection.isEnabled());
assertTrue(merge.isEnabled());
assertFalse(a.isEnabled());
assertEquals("Global", merge.getValue(Action.NAME));
setContent(project);
assertFalse(looksInGlobalSelection.isEnabled());
assertTrue(merge.isEnabled());
assertTrue(a.isEnabled());
assertEquals("DataObject", merge.getValue(Action.NAME));
assertTrue (merge.isEnabled());
merge.actionPerformed((ActionEvent) null);
looksInGlobalSelection.assertNotFired();
looksInProject.assertTargets(Collections.singleton(thing));
clearContent();
assertFalse(merge.isEnabled());
assertFalse(a.isEnabled());
setContent(project);
assertTrue(a.isEnabled());
assertFalse(looksInGlobalSelection.isEnabled());
assertTrue(merge.isEnabled());
assertEquals("DataObject", merge.getValue(Action.NAME));
clearContent();
assertEquals("Global", merge.getValue(Action.NAME));
}
public void testIndirectActionFiresEnablementCorrectly() throws Exception {
System.out.println("testIndirectActionFiresEnablementCorrectly");
E looksInProject = new E();
IndirectAction overProjectsLookup =
new IndirectAction(FakeProject.class, looksInProject, true);
E looksInGlobalSelection = new E();
looksInGlobalSelection.putValue(Action.NAME, "Global");
looksInProject.putValue(Action.NAME, "DataObject");
PCL pcl = new PCL();
overProjectsLookup.addPropertyChangeListener(pcl);
Thing thing = new Thing("thing");
FakeProject project = new FakeProject(thing);
setContent(project);
synchronized (overProjectsLookup) {
overProjectsLookup.wait(TIMEOUT);
}
synchronized (pcl) {
pcl.wait(TIMEOUT);
}
pcl.assertEnabledChangedTo(true);
}
public void testMergeFiresAppropriateChanges() throws Exception {
System.out.println("testMergeFiresAppropriateChanges");
E looksInProject = new E();
IndirectAction overProjectsLookup =
new IndirectAction(FakeProject.class, looksInProject, true);
E looksInGlobalSelection = new E();
looksInGlobalSelection.putValue(Action.NAME, "Global");
looksInProject.putValue(Action.NAME, "DataObject");
ContextAwareAction merge = ContextAction.merge(looksInGlobalSelection, overProjectsLookup);
PCL pcl = new PCL();
merge.addPropertyChangeListener(pcl);
looksInGlobalSelection.putValue("whatzit", "foo");
looksInProject.putValue("whatzit", "bar");
//We need to request it from merge at least once for it
//to know about the key and fire changes
assertEquals("foo", merge.getValue("whatzit"));
Thing thing = new Thing("thing");
FakeProject project = new FakeProject(thing);
assertFalse(merge.isEnabled());
assertFalse(looksInGlobalSelection.isEnabled());
assertFalse(looksInProject.isEnabled());
setContent(project);
assertTrue(merge.isEnabled());
assertFalse(looksInGlobalSelection.isEnabled());
assertTrue(overProjectsLookup.isEnabled());
synchronized (merge) {
merge.wait(TIMEOUT);
}
synchronized (pcl) {
pcl.wait(TIMEOUT);
}
pcl.assertEnabledChangedTo(true);
assertTrue(overProjectsLookup.isEnabled());
Object val = merge.getValue("whatzit");
assertEquals("bar", val);
assertEquals("Merge should not proxy value from disabled action when it " +
"is not disabled itself, but " +
"got " + val, "bar", val);
setContent(thing);
assertTrue(looksInGlobalSelection.isEnabled());
assertTrue(merge.isEnabled());
assertFalse(overProjectsLookup.isEnabled());
assertEquals("Global", merge.getValue(Action.NAME));
assertEquals("foo", merge.getValue("whatzit"));
synchronized (merge) {
merge.wait(TIMEOUT);
}
synchronized (pcl) {
pcl.wait(TIMEOUT);
}
assertEquals("foo", pcl.assertFired("whatzit"));
clearContent();
synchronized (merge) {
merge.wait(TIMEOUT);
}
synchronized (pcl) {
pcl.wait(TIMEOUT);
}
setContent(project);
synchronized (merge) {
merge.wait(TIMEOUT);
}
synchronized (pcl) {
pcl.wait(TIMEOUT);
}
pcl.assertFired("whatzit");
assertTrue(merge.isEnabled());
setContent(thing);
assertTrue(merge.isEnabled());
synchronized (merge) {
merge.wait(TIMEOUT);
}
synchronized (pcl) {
pcl.wait(TIMEOUT);
}
assertEquals("Global", pcl.assertFired(Action.NAME));
assertEquals("foo", pcl.assertFired("whatzit"));
}
public void testSurviveFocusChange() throws InterruptedException {
System.out.println("testSurviveFocusChange");
B b = new B();
assertFalse(b.isEnabled());
setContent("testSurviveFocusChange");
assertTrue(b.isEnabled());
PCL pcl = new PCL();
b.addPropertyChangeListener(pcl);
setContent("x");
assertTrue(b.isEnabled());
b.actionPerformed((ActionEvent) null);
b.assertPerformed("x");
clearContent();
assertTrue(b.isEnabled());
b.actionPerformed((ActionEvent) null);
b.assertPerformed("x");
setContent("a", "b");
assertFalse(b.isEnabled());
synchronized (b) {
b.wait(TIMEOUT);
}
synchronized (pcl) {
pcl.wait(TIMEOUT);
}
assertFalse(b.createContextAwareInstance(Lookup.EMPTY).isEnabled());
assertTrue(b.createContextAwareInstance(Lookups.fixed("foo")).isEnabled());
assertFalse(b.createContextAwareInstance(Lookups.fixed("moo", "goo")).isEnabled());
//
// setContent("y");
// assertTrue(b.isEnabled());
setContent(new Thing("foo"));
assertFalse("Setting selection to a non-empty lookup containing no" +
"objects of type " + b.type + " should disable a " +
"survive focus action", b.isEnabled());
}
public void testStubsDisposed() throws Exception {
System.out.println("testStubsDisposed");
E e = new E();
IndirectAction a = new IndirectAction(FakeNode.class,
new IndirectAction(FakeProject.class, e, true), true);
Thing thing1 = new Thing("a");
FakeProject proj1 = new FakeProject(thing1);
FakeNode node1 = new FakeNode(proj1);
assertNull(a.stub);
PCL pcl = new PCL();
a.addPropertyChangeListener(pcl);
Reference<IndirectAction> aref = new WeakReference<IndirectAction>(a);
Reference<E> eref = new WeakReference<E>(e);
a = null;
e = null;
assertGC("Wrapped action still referenced", eref);
assertGC("Indirect action still referenced", aref);
e = new E();
a = new IndirectAction(FakeNode.class,
new IndirectAction(FakeProject.class, e, true), true);
Set<Object> rootsHint = new HashSet<Object>(Arrays.asList(
content,
pcl));
assertNull(a.stub);
assertNull(e.stub);
pcl = new PCL();
a.addPropertyChangeListener(pcl);
assertNotNull(a.stub);
assertNotNull(e.stub);
setContent(node1);
Reference<ActionStub<?>> outerStubRef = new WeakReference<>(a.stub);
Reference<ActionStub<?>> innerStubRef = new WeakReference<>(e.stub);
//Let the change get fired before we move on; otherwise the runnable
//on the event queue can hold a reference that causes this test to
//randomly fail
synchronized (pcl) {
pcl.wait(500);
}
a.removePropertyChangeListener(pcl);
//diagnostics
Reference<PCL> pclRef = new WeakReference(pcl);
pcl = null;
assertGC("PCL still alive", pclRef, rootsHint);
assertGC("IndirectAction's stub still exists after all listeners " +
"removed", outerStubRef, rootsHint);
assertGC("Wrapped action's stub still exists after all listeners " +
"removed", innerStubRef, rootsHint);
aref = new WeakReference<>(a);
eref = new WeakReference<>(e);
a = null;
e = null;
assertGC("Wrapped action still referenced", eref, rootsHint);
assertGC("Indirect action still referenced", aref, rootsHint);
clearContent();
Reference<Thing> thingRef = new WeakReference<>(thing1);
thing1 = null;
proj1 = null;
node1 = null;
assertGC("target still referenced", thingRef, rootsHint);
}
public void testNameMutability() throws Exception {
System.out.println("testNameMutability");
F f = new F();
f.addPropertyChangeListener(new PCL()); //change() will not be called if nobody cares
assertEquals("Do something", f.getValue(Action.NAME));
Action a = f.createContextAwareInstance(Lookup.EMPTY);
assertEquals("Do something", a.getValue(Action.NAME));
System.err.println("set content to " + new Thing("X"));
setContent(new Thing("X"));
synchronized (f) {
f.wait(500);
}
System.err.println("done wait");
f.assertChanged();
String name = (String) f.getValue(Action.NAME);
assertEquals("Do something to X", name);
setContent(new Thing("X"), new Thing("Y"));
assertEquals("Do something to 2 Things", f.getValue(Action.NAME));
Action a1 = f.createContextAwareInstance(Utilities.actionsGlobalContext());
assertEquals("Do something to 2 Things", a1.getValue(Action.NAME));
Lookup other = Lookups.singleton(new Thing("Z"));
Action a2 = f.createContextAwareInstance(other);
assertEquals("Do something to Z", a2.getValue(Action.NAME));
clearContent();
assertEquals("Do something", f.getValue(Action.NAME));
assertEquals("Do something", a1.getValue(Action.NAME));
assertEquals("Do something to Z", a2.getValue(Action.NAME));
}
public void testMergeHandlesDisplayNameCorrectly() throws Exception {
System.out.println("testMergeHandlesDisplayNameCorrectly");
F f = new F();
G g = new G();
// MergeAction m = new MergeAction(new ContextAction[]{f, g});
Action m = ContextAction.merge(f, g);
m.addPropertyChangeListener(new PCL());
Thing thing1 = new Thing("tx1");
Thing thing2 = new Thing("tx2");
OtherThing otherThing1 = new OtherThing("ox1");
OtherThing otherThing2 = new OtherThing("ox2");
assertEquals("Do something", m.getValue(Action.NAME));
assertFalse(m.isEnabled());
setContent(thing1);
assertTrue(m.isEnabled());
assertEquals("Do something to tx1", m.getValue(Action.NAME));
setContent(otherThing1);
assertTrue(m.isEnabled());
assertEquals("Do something else to ox1", m.getValue(Action.NAME));
setContent(thing1);
assertTrue(m.isEnabled());
assertEquals("Do something to tx1", m.getValue(Action.NAME));
setContent(thing2);
assertTrue(m.isEnabled());
assertEquals("Do something to tx2", m.getValue(Action.NAME));
clearContent();
setContent(thing1, thing2);
assertEquals("Do something to 2 Things", m.getValue(Action.NAME));
setContent(new Object());
assertFalse(m.isEnabled());
assertEquals("Do something", m.getValue(Action.NAME));
setContent(otherThing1, otherThing2);
assertEquals("Do something else to 2 OtherThings", m.getValue(Action.NAME));
}
public void testMergeHandlesDisplayNameCorrectlyWhenNotListenedTo() throws Exception {
System.out.println("testMergeHandlesDisplayNameCorrectlyWhenNotListenedTo");
F f = new F();
G g = new G();
MergeAction m = new MergeAction(new ContextAction[]{f, g});
Thing thing1 = new Thing("tx1");
Thing thing2 = new Thing("tx2");
OtherThing otherThing1 = new OtherThing("ox1");
OtherThing otherThing2 = new OtherThing("ox2");
assertEquals("Do something", m.getValue(Action.NAME));
assertFalse(m.isEnabled());
setContent(thing1);
assertTrue(m.isEnabled());
assertEquals("Do something to tx1", m.getValue(Action.NAME));
setContent(otherThing1);
assertTrue(m.isEnabled());
assertEquals("Do something else to ox1", m.getValue(Action.NAME));
setContent(thing1);
assertTrue(m.isEnabled());
assertEquals("Do something to tx1", m.getValue(Action.NAME));
setContent(thing2);
assertTrue(m.isEnabled());
assertEquals("Do something to tx2", m.getValue(Action.NAME));
clearContent();
setContent(thing1, thing2);
assertEquals("Do something to 2 Things", m.getValue(Action.NAME));
setContent(new Object());
assertFalse(m.isEnabled());
assertEquals("Do something", m.getValue(Action.NAME));
setContent(otherThing1, otherThing2);
assertEquals("Do something else to 2 OtherThings", m.getValue(Action.NAME));
}
public void testMergeExclusivity() {
System.out.println("testMergeExclusivity");
F f = new F();
G g = new G();
MergeAction m = new MergeAction(new ContextAction[]{f, g}, true);
Thing thing1 = new Thing("tx1");
Thing thing2 = new Thing("tx2");
Thing badThing = new Thing("bad");
OtherThing otherThing1 = new OtherThing("ox1");
OtherThing otherThing2 = new OtherThing("ox2");
OtherThing otherBadThing = new OtherThing("bad");
setContent (thing1);
assertTrue (m.isEnabled());
setContent (otherThing1);
assertTrue (m.isEnabled());
setContent (thing1, otherThing1);
assertTrue (f.isEnabled());
assertTrue (g.isEnabled());
assertFalse (m.isEnabled());
setContent (thing1, otherBadThing);
assertTrue (m.isEnabled());
setContent (badThing);
assertFalse (m.isEnabled());
setContent (badThing, otherBadThing);
assertFalse (m.isEnabled());
setContent (thing1, thing2);
assertTrue (m.isEnabled());
}
private class PCL implements PropertyChangeListener {
PropertyChangeEvent evt;
Map<String, Object> fired = new HashMap<String, Object>();
void clear() {
fired.clear();
}
public void propertyChange(PropertyChangeEvent evt) {
fired.put(evt.getPropertyName(), evt.getNewValue());
if ("enabled".equals(evt.getPropertyName())) {
this.evt = evt;
}
synchronized (this) {
notifyAll();
}
}
void assertNotFired(String prop) {
assertFalse(fired.containsKey(prop));
}
public Object assertFired(String key) {
assertTrue(fired.keySet().contains(key));
return fired.remove(key);
}
void assertEnabledChangedTo(boolean val) {
PropertyChangeEvent old = this.evt;
this.evt = null;
assertNotNull(old);
Boolean b = Boolean.valueOf(val);
assertEquals("Enabled not changed to " + val + ", but " + b,
b, old.getNewValue());
}
void assertNotFired() {
assertNull(evt);
}
}
private static class A extends Single<String> {
String perfString;
A() {
super(String.class, "A", null);
}
@Override
protected void actionPerformed(String target) {
perfString = target;
}
void assertPerformed(String expected) {
String old = perfString;
perfString = null;
assertNotNull(old);
assertEquals(expected, old);
}
void assertNotPerformed() {
assertNull(perfString);
}
}
private static class B extends SurviveSelectionChange<String> {
String perfString;
B() {
super(String.class, "B", null);
}
@Override
protected void actionPerformed(Collection<? extends String> target) {
perfString = target.size() == 0 ? null : target.iterator().next();
}
void assertPerformed(String expected) {
String old = perfString;
perfString = null;
assertNotNull(old);
assertEquals(expected, old);
}
@Override
protected boolean checkQuantity(int numberOfObjects) {
return numberOfObjects == 1;
}
}
private static class C extends ContextAction<String> {
Set<String> perfStrings;
C() {
super(String.class);
}
@Override
protected boolean checkQuantity(int numberOfObjects) {
return numberOfObjects == 5;
}
void assertPerformed(Set<String> expected) {
Set<String> old = perfStrings;
perfStrings = null;
assertNotNull(old);
assertEquals(expected, old);
}
@Override
protected void actionPerformed(Collection<? extends String> targets) {
perfStrings = new HashSet<String>(targets);
}
}
private static class FakeNode implements Lookup.Provider {
private final InstanceContent content = new InstanceContent();
private final AbstractLookup lkp = new AbstractLookup(content);
FakeNode(Object... contents) {
content.set(Arrays.asList(contents), null);
}
public Lookup getLookup() {
return lkp;
}
}
private static class FakeProject implements Lookup.Provider {
private final InstanceContent content = new InstanceContent();
private final AbstractLookup lkp = new AbstractLookup(content);
FakeProject(Object... contents) {
content.set(Arrays.asList(contents), null);
}
public Lookup getLookup() {
return lkp;
}
}
private static final class Thing {
private final String s;
Thing(String s) {
this.s = s;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Thing other = (Thing) obj;
if ((this.s == null) ? (other.s != null) : !this.s.equals(other.s)) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = 3;
hash = 47 * hash + (this.s != null ? this.s.hashCode() : 0);
return hash;
}
@Override
public String toString() {
return s;
}
}
private static final class OtherThing {
private final String s;
OtherThing(String s) {
this.s = s;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final OtherThing other = (OtherThing) obj;
if ((this.s == null) ? (other.s != null) : !this.s.equals(other.s)) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = 3;
hash = 11 * hash + (this.s != null ? this.s.hashCode() : 0);
return hash;
}
@Override
public String toString() {
return s;
}
}
private static final class D extends LookupProviderAction<FakeNode, Thing> {
private Set<Thing> performedOn;
D() {
this(false);
}
D(boolean all) {
super(FakeNode.class, Thing.class, all);
}
@Override
protected void perform(Collection<? extends Thing> delegates) {
this.performedOn = new HashSet<Thing>(delegates);
}
@Override
protected boolean enabled(Collection<? extends Thing> targets) {
return !targets.contains(new Thing("no"));
}
void assertTargets(Set<Thing> expected) {
Set<Thing> old = performedOn;
performedOn = null;
assertNotNull("Expected targets " + expected + " but got " +
old, old);
assertEquals("Expected targets " + expected + " but got " +
old, expected, old);
}
}
private static final class E extends ContextAction<Thing> {
private Set<Thing> performedOn;
E() {
super(Thing.class);
}
@Override
protected void actionPerformed(Collection<? extends Thing> targets) {
this.performedOn = new HashSet<Thing>(targets);
}
@Override
protected boolean isEnabled(Collection<? extends Thing> targets) {
return !targets.contains(new Thing("no"));
}
void assertTargets(Set<Thing> expected) {
Set<Thing> old = performedOn;
performedOn = null;
assertNotNull("Expected targets " + expected + " but got " +
old, old);
assertEquals("Expected targets " + expected + " but got " +
old, expected, old);
}
void assertNotFired() {
assertNull(performedOn);
}
}
private static final class F extends ContextAction<Thing> {
F() {
super(Thing.class);
putValue(NAME, "Do something");
}
Collection<? extends Thing> targets;
void assertTargets(Collection<? extends Thing> expected) {
assertNotNull(targets);
assertEquals(expected, new HashSet<Thing>(targets));
}
@Override
protected void actionPerformed(Collection<? extends Thing> targets) {
this.targets = targets;
}
@Override
protected boolean isEnabled(Collection<? extends Thing> targets) {
for (Thing t : targets) {
if ("bad".equals(t.toString())) {
return false;
}
}
return true;
}
void assertChanged() {
boolean old = changed;
changed = false;
assertTrue ("Change not called", old);
}
boolean changed;
@Override
protected void change(Collection<? extends Thing> collection, Action instance) {
changed = true;
if (collection.size() == 0) {
instance.putValue(NAME, "Do something");
} else if (collection.size() == 1) {
instance.putValue(NAME, "Do something to " + collection.iterator().next());
} else {
instance.putValue(NAME, "Do something to " + collection.size() + " Things");
}
}
}
private static final class G extends ContextAction<OtherThing> {
G() {
super(OtherThing.class);
putValue(NAME, "Do something");
}
Collection<? extends OtherThing> targets;
void assertTargets(Collection<? extends OtherThing> expected) {
assertNotNull(targets);
assertEquals(expected, new HashSet<OtherThing>(targets));
}
@Override
protected void actionPerformed(Collection<? extends OtherThing> targets) {
this.targets = targets;
}
@Override
protected boolean isEnabled(Collection<? extends OtherThing> targets) {
for (OtherThing t : targets) {
if ("bad".equals(t.toString())) {
return false;
}
}
return true;
}
@Override
protected void change(Collection<? extends OtherThing> collection, Action instance) {
if (collection.size() == 0) {
instance.putValue(NAME, "Do something else ");
} else if (collection.size() == 1) {
instance.putValue(NAME, "Do something else to " + collection.iterator().next());
} else {
instance.putValue(NAME, "Do something else to " + collection.size() + " OtherThings");
}
}
}
}