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-2007 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.lang.reflect.InvocationTargetException;
046: import javax.swing.JFrame;
047: import org.netbeans.junit.NbTestCase;
048: import org.openide.explorer.ExplorerManager;
049: import org.openide.nodes.AbstractNode;
050: import org.openide.nodes.Children;
051: import org.openide.nodes.Node;
052: import org.openide.nodes.Sheet;
053:
054: /**
055: *
056: * @author S. Aubrecht
057: */
058: public final class TreeTableViewTest extends NbTestCase {
059:
060: private MyNodeTableModel testModel;
061:
062: public TreeTableViewTest(String testName) {
063: super (testName);
064: }
065:
066: @Override
067: protected boolean runInEQ() {
068: return true;
069: }
070:
071: /**
072: * When TreeTableView is removed from component hierarchy its model should detach property change listeners from Nodes to prevent
073: * memory leaks. When the TTV is added back to the component hierarchy, the listeners must be re-attached.
074: */
075: public void testRemoveAddNotify() throws InterruptedException {
076: MyNode[] childrenNodes = new MyNode[3];
077: for (int i = 0; i < childrenNodes.length; i++)
078: childrenNodes[i] = new MyNode();
079: Children.Array children = new Children.Array();
080: children.add(childrenNodes);
081: Node rootNode = new MyNode(children);
082:
083: TTVComponent testComponent = new TTVComponent(rootNode);
084:
085: //make sure addNotify is called on the TreeTableView
086: testComponent.pack();
087: for (int i = 0; i < childrenNodes.length; i++)
088: childrenNodes[i].forcePropertyChangeEvent();
089: assertEquals(
090: "NodeTableModel must be notified that propery values changed",
091: childrenNodes.length, testModel.tableCellUpdateCounter);
092: testModel.tableCellUpdateCounter = 0;
093:
094: //make sure removeNotify is called
095: testComponent.dispose();
096: for (int i = 0; i < childrenNodes.length; i++)
097: childrenNodes[i].forcePropertyChangeEvent();
098: assertEquals(
099: "NodeTableModel must detach listeners when the TTV is removed from component hierarchy",
100: 0, testModel.tableCellUpdateCounter);
101:
102: //make sure addNotify is called on the TreeTableView
103: testComponent.pack();
104: for (int i = 0; i < childrenNodes.length; i++)
105: childrenNodes[i].forcePropertyChangeEvent();
106: assertEquals(
107: "TTV must re-attach listeners when added back to the component hierarchy",
108: childrenNodes.length, testModel.tableCellUpdateCounter);
109: }
110:
111: final class TTVComponent extends JFrame implements
112: ExplorerManager.Provider {
113:
114: private final ExplorerManager manager = new ExplorerManager();
115:
116: TreeTableView view;
117: NodeTableModel nodeTableModel;
118:
119: private TTVComponent(Node rootNode) {
120: getRootPane().setLayout(new BorderLayout());
121: manager.setRootContext(rootNode);
122: Node[] nodes = rootNode.getChildren().getNodes();
123: testModel = new MyNodeTableModel();
124: nodeTableModel = testModel;
125: nodeTableModel.setNodes(nodes);
126:
127: Node.Property[] props = nodes[0].getPropertySets()[0]
128: .getProperties();
129:
130: nodeTableModel.setProperties(props);
131: view = new TreeTableView(nodeTableModel);
132: view.setProperties(props);
133:
134: view.setRootVisible(false);
135:
136: //Here we add the TTV to the topcomponent:
137: getRootPane().add(view, BorderLayout.CENTER);
138: }
139:
140: public ExplorerManager getExplorerManager() {
141: return manager;
142: }
143: }
144:
145: class MyNode extends AbstractNode {
146:
147: /** Creates a new instance of MyNode */
148: public MyNode() {
149: super (Children.LEAF);
150: }
151:
152: public MyNode(Children children) {
153: super (children);
154: }
155:
156: @Override
157: protected Sheet createSheet() {
158: Sheet s = super .createSheet();
159: Sheet.Set ss = s.get(Sheet.PROPERTIES);
160: if (ss == null) {
161: ss = Sheet.createPropertiesSet();
162: s.put(ss);
163: }
164: ss.put(new DummyProperty());
165: return s;
166: }
167:
168: void forcePropertyChangeEvent() {
169: firePropertyChange("unitTestPropName", null, new Object());
170: }
171:
172: class DummyProperty extends Property<Object> {
173:
174: public DummyProperty() {
175: super (Object.class);
176: setName("unitTestPropName");
177: }
178:
179: public boolean canRead() {
180: return true;
181: }
182:
183: public Object getValue() throws IllegalAccessException,
184: InvocationTargetException {
185: return getValue("unitTestPropName");
186: }
187:
188: public boolean canWrite() {
189: return true;
190: }
191:
192: public void setValue(Object val)
193: throws IllegalAccessException,
194: IllegalArgumentException, InvocationTargetException {
195: setValue("unitTestPropName", val);
196: }
197: }
198:
199: }
200:
201: class MyNodeTableModel extends NodeTableModel {
202:
203: int tableCellUpdateCounter = 0;
204:
205: @Override
206: public void fireTableCellUpdated(int row, int column) {
207: tableCellUpdateCounter++;
208: super.fireTableCellUpdated(row, column);
209: }
210: }
211: }
|