001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.openide.explorer.view;
043:
044: import java.awt.BorderLayout;
045: import java.awt.Image;
046: import java.beans.PropertyVetoException;
047: import java.lang.reflect.InvocationTargetException;
048: import javax.swing.JFrame;
049: import javax.swing.JPanel;
050: import javax.swing.SwingUtilities;
051: import junit.framework.AssertionFailedError;
052: import org.netbeans.junit.NbTestCase;
053: import org.openide.explorer.ExplorerManager;
054: import org.openide.nodes.AbstractNode;
055: import org.openide.nodes.Children;
056: import org.openide.nodes.Node;
057:
058: /**
059: * Tests for inconsistnecy in tree structures when getters change structure
060: */
061: public class TreeView48993Test extends NbTestCase {
062:
063: private static final int NO_OF_NODES = 3;
064: /** exception thrown in AWT thread */
065: private static Throwable exc;
066:
067: JFrame f;
068:
069: static {
070: System.setProperty("sun.awt.exception.handler",
071: TreeView48993Test.class.getName());
072: }
073:
074: /** Needed because we implement the handler.
075: */
076: public TreeView48993Test() {
077: super ("");
078: }
079:
080: public TreeView48993Test(String name) {
081: super (name);
082: }
083:
084: protected void setUp() {
085: exc = null;
086: }
087:
088: protected void tearDown() {
089: if (f != null) {
090: f.setVisible(false);
091: f.dispose();
092: }
093:
094: if (exc != null) {
095: AssertionFailedError ass = new AssertionFailedError(
096: "There should be no exception in AWT");
097: ass.initCause(exc);
098: throw ass;
099: }
100: }
101:
102: protected int timeOut() {
103: return 15000; // 15s
104: }
105:
106: public void testNotReentrantWhenDoingUpdate() throws Throwable {
107: f = new JFrame();
108: final MyChildren ch = new MyChildren();
109: final AbstractNode root = new AbstractNode(ch);
110: root.setName("test root");
111:
112: ch.keys(new String[] { "A", "B", "C", "D" });
113:
114: final Node[] all = ch.getNodes();
115: final UglyNode u = (UglyNode) all[2];
116:
117: SwingUtilities.invokeLater(new Runnable() {
118: public void run() {
119: Panel p = new Panel();
120: p.getExplorerManager().setRootContext(root);
121: try {
122: p.getExplorerManager().setSelectedNodes(
123: new Node[] { all[0] });
124: } catch (PropertyVetoException pve) {
125: fail(pve.toString());
126: }
127:
128: BeanTreeView btv = new BeanTreeView();
129: p.add(BorderLayout.CENTER, btv);
130:
131: f.setDefaultCloseOperation(f.EXIT_ON_CLOSE);
132: f.getContentPane().add(BorderLayout.CENTER, p);
133: f.setBounds(300, 300, 200, 200);
134: f.setVisible(true);
135: }
136: });
137:
138: u.waitOn(10000);
139:
140: waitForAWT(); // Thread.sleep(5000); // wait for the window to finish opening
141:
142: assertTrue("Somebody asked for the icon", u.flag);
143: assertEquals("Still four children", 4, ch.getNodesCount());
144:
145: ((VisualizerNode) Visualizer.findVisualizer(u)).getIcon(true,
146: true); // this clears the internal cache
147:
148: synchronized (u) {
149: u.flag = false;
150: u.keys = new String[] { "C" };
151: ch.keys(new String[] { "B", "C", "D", "A" });
152: u.fire();
153: u.waitOn(10000);
154: }
155: Thread.sleep(1000);
156:
157: assertTrue("Somebody asked for the display name again", u.flag);
158: assertEquals("Just one child", 1, ch.getNodesCount());
159: }
160:
161: public void testSomeChildrenDisappearDuringFirstPaint()
162: throws Throwable {
163: assertFalse("Won't work from AWT thread", SwingUtilities
164: .isEventDispatchThread());
165:
166: f = new JFrame();
167: final MyChildren ch = new MyChildren();
168: final AbstractNode root = new AbstractNode(ch);
169: root.setName("test root");
170:
171: ch.keys(new String[] { "A", "B", "C", "D" });
172:
173: Node[] all = ch.getNodes();
174: final UglyNode u = (UglyNode) all[1];
175:
176: synchronized (u) {
177: Panel p = new Panel();
178: p.getExplorerManager().setRootContext(root);
179: p.getExplorerManager().setSelectedNodes(
180: new Node[] { all[0] });
181:
182: BeanTreeView btv = new BeanTreeView();
183: p.add(BorderLayout.CENTER, btv);
184:
185: f.setDefaultCloseOperation(f.EXIT_ON_CLOSE);
186: f.getContentPane().add(BorderLayout.CENTER, p);
187: f.pack();
188: f.setVisible(true);
189: u.waitOn(10000000);
190:
191: Thread.sleep(2000);
192: ((VisualizerNode) Visualizer.findVisualizer(u)).getIcon(
193: true, true); // this clears the internal cache
194:
195: u.flag = false;
196: u.keys = new String[] { "B", "C", "D" };
197: }
198:
199: f.setBounds(300, 300, 200, 200);
200: f.setVisible(false);
201: f.invalidate();
202: f.validate();
203: f.repaint();
204: f.setVisible(true);
205:
206: u.waitOn(10000000); // Waits for getIcon to run
207:
208: waitForAWT();
209:
210: assertTrue("Somebody asked for the icon", u.flag);
211: assertEquals("And now we 3 ", 3, ch.getNodesCount());
212: }
213:
214: /**
215: * Waits for AWT queue to finish queued processing.
216: */
217: private static void waitForAWT() throws InterruptedException,
218: InvocationTargetException {
219: for (int i = 0; i < 5; i++) {
220: SwingUtilities.invokeAndWait(new Runnable() {
221: public void run() {
222: }
223: });
224: ;
225: }
226: }
227:
228: private static class UglyNode extends AbstractNode {
229: private boolean flag;
230: private Object[] keys;
231: boolean ticked;
232:
233: public UglyNode(String name) {
234: super (Children.LEAF);
235: setName(name);
236: }
237:
238: public String toString() {
239: return getClass().getName() + "@"
240: + Integer.toHexString(hashCode());
241:
242: }
243:
244: public Image getIcon(int type) {
245: synchronized (this ) {
246: tick();
247: flag = true;
248: if (keys != null) {
249: ((MyChildren) getParentNode().getChildren())
250: .keys(keys);
251: }
252: return super .getIcon(type);
253: }
254: }
255:
256: public void fire() {
257: fireIconChange();
258: }
259:
260: /**
261: * @return true if returning due to timeout
262: */
263: public boolean waitOn(int timeout) {
264: synchronized (this ) {
265: boolean tck;
266: if (!ticked) { //while (!ticked) {
267: try {
268: wait(timeout);
269: } catch (InterruptedException e) {
270: throw new InternalError();
271: }
272: }
273: tck = ticked;
274: ticked = false; // reusable
275: return !tck;
276: }
277: }
278:
279: public void tick() {
280: synchronized (this ) {
281: ticked = true;
282: notifyAll();
283: }
284: }
285:
286: }
287:
288: public void testRemoveOneOfEqualsChildren() throws Throwable {
289: class EqualsNode extends AbstractNode {
290: private Object token;
291:
292: public EqualsNode(String name, Object token) {
293: super (Children.LEAF);
294: this .token = token;
295: setName(name);
296: }
297:
298: public boolean equals(Object obj) {
299: if (obj instanceof EqualsNode) {
300: return ((EqualsNode) obj).token.equals(token);
301: }
302: return super .equals(obj);
303: }
304: }
305:
306: class EqualsChildren extends Children.Keys {
307: Object token;
308:
309: EqualsChildren(Object token) {
310: this .token = token;
311: }
312:
313: protected Node[] createNodes(Object key) {
314: return new Node[] { new EqualsNode(key.toString(),
315: token) };
316: }
317:
318: public void keys(Object[] keys) {
319: setKeys(keys);
320: }
321: }
322:
323: Object token = new Object();
324: assertFalse("Won't work from AWT thread", SwingUtilities
325: .isEventDispatchThread());
326:
327: EqualsChildren ch = new EqualsChildren(token);
328: AbstractNode root = new AbstractNode(ch);
329: root.setName("test root");
330:
331: Panel p = new Panel();
332: p.getExplorerManager().setRootContext(root);
333: BeanTreeView btv = new BeanTreeView();
334: p.add(BorderLayout.CENTER, btv);
335:
336: f = new JFrame();
337: f.setDefaultCloseOperation(f.EXIT_ON_CLOSE);
338: f.getContentPane().add(BorderLayout.CENTER, p);
339: f.pack();
340: f.setVisible(true);
341: waitForAWT();
342:
343: ch.keys(new String[] { "A", "B" });
344:
345: waitForAWT();
346:
347: ch.keys(new String[] { "a", "B" });
348:
349: waitForAWT();
350: }
351:
352: private static class MyChildren extends Children.Keys {
353: protected Node[] createNodes(Object key) {
354: return new Node[] { new UglyNode(key.toString()) };
355: }
356:
357: public void keys(Object[] keys) {
358: setKeys(keys);
359: }
360: }
361:
362: /** The name MUST be handle and MUST be public */
363: public static void handle(Throwable t) {
364: exc = t;
365: }
366:
367: private static class Panel extends JPanel implements
368: ExplorerManager.Provider {
369: private ExplorerManager em = new ExplorerManager();
370:
371: public ExplorerManager getExplorerManager() {
372: return em;
373: }
374: }
375: }
|