blob: 838385242d459390b811d9bc606c61448c99f75f [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.openide.windows;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.DefaultKeyboardFocusManager;
import java.awt.EventQueue;
import java.awt.GraphicsEnvironment;
import java.awt.KeyboardFocusManager;
import java.awt.event.ActionEvent;
import java.beans.FeatureDescriptor;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.JButton;
import javax.swing.JTextField;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import junit.framework.Test;
import junit.framework.TestSuite;
import org.netbeans.junit.NbTestCase;
import org.openide.awt.Actions;
import org.openide.cookies.CloseCookie;
import org.openide.cookies.EditCookie;
import org.openide.cookies.OpenCookie;
import org.openide.cookies.SaveCookie;
import org.openide.cookies.ViewCookie;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.Children;
import org.openide.nodes.FilterNode;
import org.openide.nodes.Node;
import org.openide.util.ContextAwareAction;
import org.openide.util.Exceptions;
import org.openide.util.Lookup;
import org.openide.util.Lookup.Result;
import org.openide.util.LookupEvent;
import org.openide.util.LookupListener;
import org.openide.util.Utilities;
import org.openide.util.lookup.AbstractLookup;
import org.openide.util.lookup.InstanceContent;
/**
* Check the behaviour of TopComponent's lookup.
* @author Jaroslav Tulach, Jesse Glick
*/
public class TopComponentGetLookupTest extends NbTestCase {
public static Test suite() {
return GraphicsEnvironment.isHeadless() ? new TestSuite() : new TestSuite(TopComponentGetLookupTest.class);
}
static {
System.setProperty("org.openide.windows.DummyWindowManager.VISIBLE", "false");
}
/** top component we call set on*/
protected TopComponent top;
/** top component we call get on */
protected TopComponent get;
/** its lookup */
protected Lookup lookup;
private Logger LOG = Logger.getLogger("TEST-" + getName());
public TopComponentGetLookupTest(String testName) {
super(testName);
}
/** Setup component with lookup.
*/
@Override
protected void setUp() {
top = new TopComponent();
get = top;
lookup = top.getLookup();
defaultFocusManager.setC(null);
}
@Override
protected boolean runInEQ() {
return true;
}
@Override
protected Level logLevel() {
return Level.FINER;
}
/** Test to find nodes.
*/
private <T> void doTestNodes(Node[] arr, Class<T> c, int cnt) {
LOG.fine("setActivatedNodes: " + arr);
if (arr != null) {
top.setActivatedNodes(arr);
}
assertNotNull("At least one node is registered", lookup.lookup(c));
LOG.fine("before lookup");
Lookup.Result<T> res = lookup.lookup(new Lookup.Template<T>(c));
Collection<? extends Lookup.Item<T>> coll = res.allItems();
LOG.fine("after lookup");
assertEquals("Two registered: " + coll, cnt, coll.size());
}
public void testNodes() {
doTestNodes(new Node[] {new N("1"), new N("2")}, N.class, 2);
doTestNodes(new Node[] {new N("1"), new N("2")}, FeatureDescriptor.class, 2);
}
private <T> void doTestNodesWithChangesInLookup(Class<T> c) {
InstanceContent ic = new InstanceContent();
LOG.fine("do test nodes");
Node[] arr = new Node[] {
new AbstractNode(Children.LEAF, new AbstractLookup(ic)),
new AbstractNode(Children.LEAF, Lookup.EMPTY),
};
arr[0].setName("cookie-container-node");
arr[1].setName("node-as-cookie");
//doTestNodes(arr, AbstractNode.class);
doTestNodes(arr, c, 2);
LOG.fine("add new node");
ic.add(arr[1]);
/* Huh? There should be both [0] and [1], how can you say which one will be returned?
assertEquals ("Now the [1] is in lookup of [0]", arr[1], lookup.lookup (c));
*/
Collection<? extends T> all = lookup.lookup(new Lookup.Template<T>(c)).allInstances();
LOG.fine("query");
assertEquals("Two nodes are in TC lookup", 2, all.size());
assertEquals("They are the ones we expect", new HashSet<Node>(Arrays.asList(arr)), new HashSet<T>(all));
assertTrue("Lookup simple query gives one or the other", new HashSet<Node>(Arrays.asList(arr)).contains(lookup.lookup(c)));
assertEquals("Have two lookup items", 2, lookup.lookup(new Lookup.Template<T>(c)).allItems().size());
LOG.fine("last doTestNodes");
doTestNodes(null, c, 2);
LOG.fine("after last doTestNodes");
}
public void testNodesWhenTheyAreNotInTheirLookup() {
doTestNodesWithChangesInLookup(AbstractNode.class);
}
public void testNodesSuperclassesWhenTheyAreNotInTheirLookup() {
doTestNodesWithChangesInLookup(FeatureDescriptor.class);
}
public void testFilterNodeProblems() {
class CookieN extends AbstractNode implements Node.Cookie {
public CookieN() {
super(Children.LEAF);
getCookieSet().add(this);
}
}
CookieN n = new CookieN();
FilterNode fn = new FilterNode(n);
top.setActivatedNodes(new Node[] { fn });
assertTrue("CookieN is in FilterNode lookup", n == fn.getLookup().lookup(CookieN.class));
assertTrue("CookieN is in TopComponent", n == lookup.lookup(CookieN.class));
assertEquals("Just one node", 1, lookup.lookup(new Lookup.Template<Node>(Node.class)).allItems().size());
assertTrue("Plain cookie found", n == lookup.lookup(Node.Cookie.class));
}
/** Tests changes in cookies.
*/
public void testCookies() {
N[] arr = { new N("1"), new N("2"), new N("3") };
LOG.fine("before activating");
top.setActivatedNodes(arr);
LOG.fine("After activating");
Node[] myArr = get.getActivatedNodes();
LOG.fine("Nodes got back: " + myArr + " from " + top);
if (myArr != null) {
LOG.fine(" here they are: " + Arrays.asList(myArr));
}
assertEquals("Three nodes there", 3, myArr.length);
LOG.fine("Correctly activated");
L l = new L();
Lookup.Result<OpenCookie> res = lookup.lookup(new Lookup.Template<OpenCookie>(OpenCookie.class));
res.addLookupListener(l);
LOG.fine("Listener attached");
assertEquals("Empty now", res.allItems().size(), 0);
LOG.fine("Changing state to 0x01");
arr[0].state(0x01); // check open cookie
LOG.fine("Changing state to 0x01 done");
assertEquals("One item", res.allItems().size(), 1);
l.check("One change", 1);
LOG.fine("Changing state to 0x02");
arr[2].state(0x02); // change of different cookie
LOG.fine("Changing state to 0x02 done");
assertEquals("Still one item", res.allItems().size(), 1);
l.check("No change", 0);
LOG.fine("Changing state to 0x03");
arr[2].state(0x03); // added also OpenCookie
LOG.fine("Changing state to 0x03 done");
assertEquals("Both items", res.allItems().size(), 2);
l.check("One change again", 1);
LOG.fine("Changing state to 0x00");
arr[0].state(0x00);
LOG.fine("Changing state to 0x00 done");
assertEquals("One still there", res.allItems().size(), 1);
assertEquals("The second object", lookup.lookup(OpenCookie.class), arr[2].getCookie(OpenCookie.class));
LOG.fine("Clearing activated nodes");
top.setActivatedNodes(new Node[0]);
LOG.fine("Clear done");
assertNull("No cookie now", lookup.lookup(OpenCookie.class));
}
public void testNodesAreInTheLookupAndNothingIsFiredBeforeFirstQuery() {
AbstractNode n1 = new AbstractNode(Children.LEAF, Lookup.EMPTY);
top.setActivatedNodes(new Node[] { n1 });
assertEquals("One node there", 1, get.getActivatedNodes().length);
assertEquals("Is the right now", n1, get.getActivatedNodes()[0]);
Lookup.Result<Node> res = lookup.lookup(new Lookup.Template<Node>(Node.class));
L l = new L();
res.addLookupListener(l);
l.check("Nothing fired before first query", 0);
res.allInstances();
l.check("Nothing is fired on first query", 0);
lookup.lookup(new Lookup.Template<Node>(Node.class)).allInstances();
l.check("And additional query does not change anything either", 0);
}
public void testNodesAreThereEvenIfTheyAreNotContainedInTheirOwnLookup() {
Lookup.Result<Node> res = lookup.lookup(new Lookup.Template<Node>(Node.class));
AbstractNode n1 = new AbstractNode(Children.LEAF, Lookup.EMPTY);
InstanceContent content = new InstanceContent();
AbstractNode n2 = new AbstractNode(Children.LEAF, new AbstractLookup(content));
assertNull("Not present in its lookup", n1.getLookup().lookup(n1.getClass()));
assertNull("Not present in its lookup", n2.getLookup().lookup(n2.getClass()));
top.setActivatedNodes(new AbstractNode[] { n1 });
assertEquals("But node is in the lookup", n1, lookup.lookup(n1.getClass()));
assertEquals("One item there", 1, res.allInstances().size());
L listener = new L();
res.addLookupListener(listener);
top.setActivatedNodes(new AbstractNode[] { n2 });
assertEquals("One node there", 1, get.getActivatedNodes().length);
assertEquals("n2", n2, get.getActivatedNodes()[0]);
//MK - here it changes twice.. because the setAtivatedNodes is trigger on inner TC, then lookup of MVTC contains old activated node..
// at this monent the merged lookup contains both items.. later it gets synchronized by setting the activated nodes on the MVTC as well..
// then it contains only the one correct node..
listener.check("Node changed", 1);
Collection addedByTCLookup = res.allInstances();
assertEquals("One item still", 1, addedByTCLookup.size());
content.add(n2);
assertEquals("After the n2.getLookup starts to return itself, there is no change",
addedByTCLookup, res.allInstances());
// this could be commented out if necessary:
listener.check("And nothing is fired", 0);
content.remove(n2);
assertEquals("After the n2.getLookup stops to return itself, there is no change",
addedByTCLookup, res.allInstances());
// this could be commented out if necessary:
listener.check("And nothing is fired", 0);
content.add(n1);
// this could be commented out if necessary:
listener.check("And nothing is fired", 0);
// Change from former behavior (#36336): we don't *want* n1 in res.
Collection one = res.allInstances();
assertEquals("Really just the activated node", 1, one.size());
Iterator it = one.iterator();
assertEquals("It is the one added by the TC lookup", n2, it.next());
}
public void testNoChangeWhenSomethingIsChangedOnNotActivatedNode() {
doTestNoChangeWhenSomethingIsChangedOnNotActivatedNode(0);
}
public void testNoChangeWhenSomethingIsChangedOnNotActivatedNode2() {
doTestNoChangeWhenSomethingIsChangedOnNotActivatedNode(50);
}
private void doTestNoChangeWhenSomethingIsChangedOnNotActivatedNode(int initialSize) {
Object obj = new OpenCookie() { public void open() {} };
Lookup.Result<OpenCookie> res = lookup.lookup(new Lookup.Template<OpenCookie>(OpenCookie.class));
Lookup.Result<Node> nodeRes = lookup.lookup(new Lookup.Template<Node>(Node.class));
InstanceContent ic = new InstanceContent();
CountingLookup cnt = new CountingLookup(ic);
AbstractNode ac = new AbstractNode(Children.LEAF, cnt);
for (int i = 0; i < initialSize; i++) {
ic.add(new Integer(i));
}
top.setActivatedNodes(new Node[] { ac });
assertEquals("One node there", 1, get.getActivatedNodes().length);
assertEquals("It is the ac one", ac, get.getActivatedNodes()[0]);
ic.add(obj);
L listener = new L();
res.allItems();
nodeRes.allItems();
res.addLookupListener(listener);
Collection allListeners = cnt.listeners;
assertEquals("Has the cookie", 1, res.allItems().size());
listener.check("No changes yet", 0);
ic.remove(obj);
assertEquals("Does not have the cookie", 0, res.allItems().size());
listener.check("One change", 1);
top.setActivatedNodes(new N[0]);
assertEquals("The nodes are empty", 0, get.getActivatedNodes().length);
listener.check("No change", 0);
cnt.queries = 0;
ic.add(obj);
ic.add(ac);
listener.check("Removing the object or node from not active node does not send any event", 0);
nodeRes.allItems();
listener.check("Queriing for node does generate an event", 0);
assertEquals("No Queries to the not active node made", 0, cnt.queries);
assertEquals("No listeneners on cookies", allListeners, cnt.listeners);
}
public void testBug32470FilterNodeAndANodeImplementingACookie() {
class NY extends AbstractNode implements SaveCookie {
public NY() {
super(Children.LEAF);
getCookieSet().add(this);
}
public void save() {
}
}
Node ny = new NY();
Node node = new FilterNode(new FilterNode(ny, null, ny.getLookup()));
top.setActivatedNodes(new Node[] { node });
Lookup.Template<Node> nodeTemplate = new Lookup.Template<Node>(Node.class);
Lookup.Template<SaveCookie> saveTemplate = new Lookup.Template<SaveCookie>(SaveCookie.class);
Collection<? extends Node> res;
res = lookup.lookup(nodeTemplate).allInstances();
assertEquals("just one returned", res.size(), 1);
assertEquals("node is node", node, res.iterator().next());
//MK - the above 2 tests should test the same..
// assertEquals ("FilterNode is the only node there",
// Collections.singletonList(node), res
// );
Collection<? extends SaveCookie> res2 = lookup.lookup(saveTemplate).allInstances();
assertEquals("just one returned", res2.size(), 1);
assertEquals("node is node", ny, res2.iterator().next());
//MK - the above 2 tests should test the same..
// assertEquals ("SaveCookie is there only once",
// Collections.singletonList(ny), res
// );
res = lookup.lookup(nodeTemplate).allInstances();
assertEquals("just one returned", res.size(), 1);
assertEquals("node is node", node, res.iterator().next());
//MK - the above 2 tests should test the same..
// assertEquals ("FilterNode is still the only node there",
// Collections.singletonList(node), res
// );
}
public void testActionMapIsTakenFromComponentAndAlsoFromFocusedOne() {
JTextField panel = new JTextField();
defaultFocusManager.setC(panel);
top.add(BorderLayout.CENTER, panel);
class Act extends AbstractAction {
public void actionPerformed(ActionEvent ev) {
}
}
Act act1 = new Act();
Act act2 = new Act();
Act act3 = new Act();
top.getActionMap().put("globalRegistration", act1);
top.getActionMap().put("doubleRegistration", act2);
panel.getActionMap().put("doubleRegistration", act3);
panel.getActionMap().put("focusedRegistration", act3);
final ActionMap map = (ActionMap)top.getLookup().lookup(ActionMap.class);
assertEquals("actions registered directly on TC are found", act1, map.get("globalRegistration"));
assertEquals("even if they are provided by focused component", act2, map.get("doubleRegistration"));
assertEquals("actions are delegated to focus owner, if not present", act3, map.get("focusedRegistration"));
List<Object> keys = Arrays.asList(map.allKeys());
assertTrue("focusedRegistration is there: " + keys, keys.contains("focusedRegistration"));
assertTrue("doubleRegistration is there: " + keys, keys.contains("doubleRegistration"));
assertTrue("globalRegistration is there: " + keys, keys.contains("globalRegistration"));
assertTrue("closeWindow is by default there: " + keys, keys.contains("closeWindow"));
top.open();
top.requestActive();
final Result<ActionMap> res = Utilities.actionsGlobalContext().lookupResult(ActionMap.class);
class Checker implements LookupListener {
List<Object> keys1, keys2;
@Override
public void resultChanged(LookupEvent ev) {
Object[] arr = map.allKeys();
if (arr == null) {
arr = new Object[0];
}
if (keys1 == null) {
keys1 = Arrays.asList(arr);
} else {
assertNull("At most two calls", keys2);
keys2 = Arrays.asList(arr);
}
}
}
Checker listener = new Checker();
res.addLookupListener(listener);
assertEquals("One map", 1, res.allInstances().size());
JTextField f = new JTextField();
f.getActionMap().put("focusedRegistration", act3);
defaultFocusManager.setC(f);
assertEquals("but as it is not in the right component, nothing is found", null, map.get("focusedRegistration"));
res.removeLookupListener(listener);
assertNotNull("Two changes delivered", listener.keys2);
assertTrue("doubleRegistration was there: " + listener.keys1, listener.keys1.contains("doubleRegistration"));
assertTrue("globalRegistration was there: " + listener.keys1, listener.keys1.contains("globalRegistration"));
assertTrue("closeWindow was there: " + listener.keys1, listener.keys1.contains("closeWindow"));
assertTrue("focusedRegistration was there: " + listener.keys1, listener.keys1.contains("focusedRegistration"));
assertFalse("focusedRegistration was there: " + listener.keys2, listener.keys2.contains("focusedRegistration"));
assertTrue("doubleRegistration was there: " + listener.keys2, listener.keys2.contains("doubleRegistration"));
assertTrue("globalRegistration was there: " + listener.keys2, listener.keys2.contains("globalRegistration"));
assertTrue("closeWindow was there: " + listener.keys2, listener.keys2.contains("closeWindow"));
}
public void testActionMapFromFocusedOneButNotOwnButton() {
ContextAwareAction a = Actions.callback("find", null, false, "Find", null, false);
class Act extends AbstractAction {
int cnt;
Action check;
@Override
public void actionPerformed(ActionEvent ev) {
cnt++;
}
}
Act act1 = new Act();
Act act3 = new Act();
Action action = a;
act1.check = action;
act3.check = action;
final JButton disabled = new JButton();
top.add(BorderLayout.CENTER, disabled);
final JButton f = new JButton(action);
class L implements PropertyChangeListener {
private int cnt;
@Override
public void propertyChange(PropertyChangeEvent evt) {
assertEquals("enabled", evt.getPropertyName());
cnt++;
}
}
L listener = new L();
action.addPropertyChangeListener(listener);
top.add(BorderLayout.SOUTH, f);
defaultFocusManager.setC(top);
disabled.getActionMap().put("find", act3);
top.open();
top.requestActive();
assertFalse("Disabled by default", action.isEnabled());
assertFalse("Button disabled too", f.isEnabled());
assertEquals("no change yet", 0, listener.cnt);
defaultFocusManager.setC(disabled);
assertEquals("One change", 1, listener.cnt);
assertTrue("Still enabled", action.isEnabled());
assertTrue("Button enabled too", f.isEnabled());
f.getModel().addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
if (f.getModel().isPressed()) {
defaultFocusManager.setC(f);
} else {
defaultFocusManager.setC(disabled);
}
}
});
f.doClick();
assertEquals("Not Delegated to act1", 0, act1.cnt);
assertEquals("Delegated to act3", 1, act3.cnt);
}
public void testChangingNodesDoesNotChangeActionMap() {
N node = new N("testChangingNodesDoesNotChangeActionMap");
node.state(0x00);
top.setActivatedNodes(new Node[] { node });
Lookup.Result<ActionMap> res = lookup.lookup(new Lookup.Template<ActionMap>(ActionMap.class));
assertEquals("One item there", 1, res.allInstances().size());
ActionMap map = (ActionMap)res.allInstances().toArray()[0];
L l = new L();
res.addLookupListener(l);
node.state(0x01);
assertEquals("Map is still the same", map, res.allInstances().toArray()[0]);
l.check("No change in lookup", 0);
top.setActivatedNodes(new Node[] { Node.EMPTY });
assertEquals("Map remains the same", map, res.allInstances().toArray()[0]);
l.check("There is no change", 0);
}
public void testMapKeys45323() {
assertNotNull(top.getActionMap().keys());
}
/**
* Check that even if a node has a <em>different</em> node in its lookup, a
* query on Node.class will produce only the actual activated nodes.
* Other queries may return the embedded node, but not duplicates.
* @see "#36336"
*/
public void testForeignNodesInLookupIgnoredForNodeQuery() throws Exception {
class CloseCookieNode extends AbstractNode implements CloseCookie {
CloseCookieNode() {
super(Children.LEAF);
setName("n1");
}
public boolean close() {return true;}
}
Node n1 = new CloseCookieNode();
Node n2 = new AbstractNode(Children.LEAF) {
{
setName("n2");
class ViewCookieNode extends AbstractNode implements ViewCookie {
ViewCookieNode() {
super(Children.LEAF);
setName("n3");
}
public void view() {}
}
getCookieSet().add(new ViewCookieNode());
getCookieSet().add(new OpenCookie() {
public void open() {}
});
}
};
Node[] sel = new Node[] {n1, n2};
assertEquals("First node in selection has CloseCookie",
1,
n1.getLookup().lookup(new Lookup.Template<CloseCookie>(CloseCookie.class)).allInstances().size());
assertEquals("Second node in selection has OpenCookie",
1,
n2.getLookup().lookup(new Lookup.Template<OpenCookie>(OpenCookie.class)).allInstances().size());
assertEquals("Second node in selection has ViewCookie (actually a Node)",
1,
n2.getLookup().lookup(new Lookup.Template<ViewCookie>(ViewCookie.class)).allInstances().size());
ViewCookie v = (ViewCookie)n2.getCookie(ViewCookie.class);
assertNotNull(v);
assertTrue(v instanceof Node);
HashSet<Node> queryJustOnce = new HashSet<Node>(n2.getLookup().lookup(new Lookup.Template<Node>(Node.class)).allInstances());
assertEquals("Second node in selection has two nodes in its own lookup",
new HashSet<Object>(Arrays.asList(new Object[] {n2, v})), queryJustOnce
);
assertEquals(2, queryJustOnce.size());
top.setActivatedNodes(sel);
assertEquals("CloseCookie propagated from one member of node selection to TC lookup",
1,
lookup.lookup(new Lookup.Template<CloseCookie>(CloseCookie.class)).allInstances().size());
assertEquals("OpenCookie propagated from one member of node selection to TC lookup",
1,
lookup.lookup(new Lookup.Template<OpenCookie>(OpenCookie.class)).allInstances().size());
assertEquals("ViewCookie propagated from one member of node selection to TC lookup",
1,
lookup.lookup(new Lookup.Template<ViewCookie>(ViewCookie.class)).allInstances().size());
assertEquals("But TC lookup query on Node gives only selection, not cookie node",
new HashSet<Node>(Arrays.asList(sel)),
new HashSet<Node>(lookup.lookup(new Lookup.Template<Node>(Node.class)).allInstances()));
assertEquals(2, lookup.lookup(new Lookup.Template<Node>(Node.class)).allInstances().size());
assertEquals("TC lookup query on FeatureDescriptor gives all three however",
3,
lookup.lookup(new Lookup.Template<FeatureDescriptor>(FeatureDescriptor.class)).allInstances().size());
top.setActivatedNodes(new Node[] {n1});
assertEquals("After setting node selection to one node, TC lookup has only that node",
Collections.singleton(n1),
new HashSet<Node>(lookup.lookup(new Lookup.Template<Node>(Node.class)).allInstances()));
assertEquals(1, lookup.lookup(new Lookup.Template<Node>(Node.class)).allInstances().size());
assertEquals("And the OpenCookie is gone",
0,
lookup.lookup(new Lookup.Template<OpenCookie>(OpenCookie.class)).allInstances().size());
assertEquals("And the ViewCookie is gone",
0,
lookup.lookup(new Lookup.Template<ViewCookie>(ViewCookie.class)).allInstances().size());
assertEquals("But the CloseCookie remains",
1,
lookup.lookup(new Lookup.Template<CloseCookie>(CloseCookie.class)).allInstances().size());
}
public void testAssociateLookupCanBecalledJustOnce() throws Exception {
class TC extends TopComponent {
public TC() {
}
public TC(Lookup l) {
super(l);
}
public void asso(Lookup l) {
associateLookup(l);
}
}
TC tc = new TC();
assertNotNull("There is default lookup", tc.getLookup());
try {
tc.asso(Lookup.EMPTY);
fail("Should throw an exception");
} catch (IllegalStateException ex) {
// ok, should be thrown
}
tc = new TC(Lookup.EMPTY);
assertEquals("Should return the provided lookup", Lookup.EMPTY, tc.getLookup());
try {
tc.asso(Lookup.EMPTY);
fail("Should throw an exception - second association not possible");
} catch (IllegalStateException ex) {
// ok, should be thrown
}
tc = new TC();
tc.asso(Lookup.EMPTY);
assertEquals("First association was successful", Lookup.EMPTY, tc.getLookup());
try {
tc.asso(new TC().getLookup());
fail("Should throw an exception - second association not possible");
} catch (IllegalStateException ex) {
// ok, should be thrown
}
}
/** Listener to count number of changes.
*/
private static final class L extends Object
implements LookupListener {
private int cnt;
/** A change in lookup occured.
* @param ev event describing the change
*/
public void resultChanged(LookupEvent ev) {
cnt++;
}
/** Checks at least given number of changes.
*/
public void checkAtLeast(String text, int num) {
if (cnt < num) {
fail(text + " expected at least " + num + " but was " + cnt);
}
cnt = 0;
}
/** Checks number of modifications.
*/
public void check(String text, int num) {
assertEquals(text, num, cnt);
cnt = 0;
}
}
/** Overides some methods so it is not necessary to use the data object.
*/
protected static final class N extends AbstractNode {
private Node.Cookie[] cookies = {
new OpenCookie() { public void open() {} },
new EditCookie() { public void edit() {} },
new SaveCookie() { public void save() {} },
new CloseCookie() { public boolean close() { return true; } },
};
private int s;
public N(String name) {
super(Children.LEAF);
setName(name);
}
public void state(int s) {
this.s = s;
fireCookieChange();
}
public <T extends Node.Cookie> T getCookie(Class<T> c) {
int mask = 0x01;
for (int i = 0; i < cookies.length; i++) {
if ((s & mask) != 0 && c.isInstance(cookies[i])) {
return c.cast( cookies[i] );
}
mask = mask << 1;
}
return null;
}
public String toString() {
return "N[" + getName() + ", " + s + "]";
}
}
private static final class CountingLookup extends Lookup {
private Lookup delegate;
public List<LookupListener> listeners = new ArrayList<LookupListener>();
public int queries;
public CountingLookup(InstanceContent ic) {
delegate = new AbstractLookup(ic);
}
public <T> T lookup(Class<T> clazz) {
return delegate.lookup(clazz);
}
public <T> Lookup.Result<T> lookup(Lookup.Template<T> template) {
if (
!Node.Cookie.class.isAssignableFrom(template.getType()) &&
!Node.class.isAssignableFrom(template.getType())
) {
return delegate.lookup(template);
}
final Lookup.Result<T> d = delegate.lookup(template);
class Wrap extends Lookup.Result<T> {
public void addLookupListener(LookupListener l) {
listeners.add(l);
d.addLookupListener(l);
}
public void removeLookupListener(LookupListener l) {
listeners.remove(l);
d.removeLookupListener(l);
}
public Collection<? extends T> allInstances() {
queries++;
return d.allInstances();
}
public Collection<? extends Lookup.Item<T>> allItems() {
queries++;
return d.allItems();
}
public Set<Class<? extends T>> allClasses() {
queries++;
return d.allClasses();
}
}
return new Wrap();
}
}
static class Def extends DefaultKeyboardFocusManager {
private Component c;
private int cnt;
public Def() {
KeyboardFocusManager.setCurrentKeyboardFocusManager(this);
}
public void setC(Component c) {
Object oldC = this.c;
this.c = c;
firePropertyChange("permanentFocusOwner", oldC, c);
}
@Override
public Component getFocusOwner() {
return null;
}
@Override
public Component getPermanentFocusOwner() {
cnt++;
return c;
}
}
private final static Def defaultFocusManager = new Def();
static {
try {
Utilities.actionsGlobalContext();
EventQueue.invokeAndWait(new Runnable() {
@Override public void run() {}
});
assertEquals("actionsGlobalContext calls once into our focus manager", 1, defaultFocusManager.cnt);
} catch (InterruptedException | InvocationTargetException ex) {
throw new IllegalStateException(ex);
}
}
}