1 /* 2 * $Id$ 3 * 4 * Copyright (c) 2001, 2009, Oracle and/or its affiliates. All rights reserved. 5 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 6 * 7 * This code is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License version 2 only, as 9 * published by the Free Software Foundation. Oracle designates this 10 * particular file as subject to the "Classpath" exception as provided 11 * by Oracle in the LICENSE file that accompanied this code. 12 * 13 * This code is distributed in the hope that it will be useful, but WITHOUT 14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16 * version 2 for more details (a copy is included in the LICENSE file that 17 * accompanied this code). 18 * 19 * You should have received a copy of the GNU General Public License version 20 * 2 along with this work; if not, write to the Free Software Foundation, 21 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 22 * 23 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 24 * or visit www.oracle.com if you need additional information or have any 25 * questions. 26 */ 27 package com.sun.javatest.exec; 28 29 import java.awt.EventQueue; 30 import java.util.Enumeration; 31 32 import javax.swing.JTree; 33 import javax.swing.ToolTipManager; 34 import javax.swing.event.TreeModelEvent; 35 import javax.swing.event.TreeModelListener; 36 import javax.swing.tree.TreeModel; 37 import javax.swing.tree.TreePath; 38 39 import com.sun.javatest.Parameters; 40 import com.sun.javatest.TestFilter; 41 import com.sun.javatest.TestResult; 42 import com.sun.javatest.TestResultTable; 43 import com.sun.javatest.tool.UIFactory; 44 import com.sun.javatest.util.Debug; 45 import com.sun.javatest.util.DynamicArray; 46 import javax.swing.tree.TreeSelectionModel; 47 48 /** 49 * Tree to render test structure. 50 */ 51 class TestTree extends JTree { 52 /** 53 * @param uif The UI factory object to use. 54 * @param model The GUI model object. 55 * @param fConfig The active filter configuration object. 56 * @param treeModel The data model. 57 */ 58 public TestTree(UIFactory uif, TreePanelModel model, FilterSelectionHandler fh, 59 TestTreeModel treeModel) { 60 super(); 61 62 this.uif = uif; 63 this.tpm = model; 64 this.filterHandler = fh; 65 66 filterHandler.addObserver(watcher); 67 68 //currModel = new TestTreeModel(null); 69 currModel = treeModel; 70 setModel(currModel); 71 setLargeModel(true); 72 setScrollsOnExpand(true); 73 setName("tree"); 74 ToolTipManager.sharedInstance().registerComponent(this); 75 } 76 77 /** 78 * Call when it is likely that the Parameters or filters have changed internally. 79 */ 80 public void updateGUI() { 81 // delete this if the model becomes top-level 82 83 // invalidate the node info cache because we don't know what part 84 // of the parameters have changed. A filter change is likely if 85 // the user has just completed an interview 86 repaint(); 87 } 88 89 public void setModel(TreeModel newModel) { 90 super.setModel(newModel); 91 newModel.addTreeModelListener(watcher); 92 } 93 94 // ---------- private ---------- 95 void setParameters(Parameters p) { 96 // this is only here to notify the model when we change things which 97 // affect the tree structure itself. 98 // otherwise, most changes are the result of filter changes 99 } 100 101 /** 102 * For proper operation, this method is recommended over the method on JTree. 103 * 104 * @param mod The new data model. 105 */ 106 void setTreeModel(TestTreeModel mod) { 107 if (currModel != null) 108 currModel.removeTreeModelListener(watcher); 109 110 currModel = mod; 111 setModel(currModel); 112 } 113 114 TestResultTable getTestResultTable() { 115 // remove this method when the model becomes independent 116 return currModel.getTestResultTable(); 117 } 118 119 TreePath[] snapshotSelectedPaths() { 120 121 TreeSelectionModel tsm = getSelectionModel(); 122 if (tsm == null) return null; 123 124 return tsm.getSelectionPaths(); 125 126 } 127 128 void restoreSelection(String[] selectedUrls) { 129 if (selectedUrls == null || selectedUrls.length == 0) 130 return; 131 132 if (currModel instanceof TestTreeModel) { 133 final TreePath[] paths = currModel.urlsToPaths(selectedUrls); 134 135 if (!EventQueue.isDispatchThread()) { 136 EventQueue.invokeLater(new Runnable() { 137 @Override 138 public void run() { 139 restoreSelection(paths); 140 } 141 142 }); 143 } 144 else 145 restoreSelection(paths); 146 } 147 } 148 149 private void restoreSelection(TreePath[] selectedPaths) { 150 if (selectedPaths == null || selectedPaths.length == 0) 151 return; 152 153 TreeSelectionModel tsm = getSelectionModel(); 154 if (tsm == null) 155 return; 156 157 // add or set ??? 158 tsm.addSelectionPaths(selectedPaths); 159 } 160 161 162 /** 163 * Record which tree nodes are currently visible. 164 * @see #restorePaths 165 */ 166 TreePath[] snapshotOpenPaths() { 167 // currModel.getRoot()==null may indicate that the GUI has been 168 // disposed already (?). We have seen exceptions in the code 169 // below because this situation occurs. 170 if (currModel == null || currModel.getRoot() == null) 171 return null; 172 173 TreePath[] paths = new TreePath[0]; 174 Enumeration e = getDescendantToggledPaths(new TreePath(currModel.getRoot())); 175 176 while (e != null && e.hasMoreElements()) { 177 TreePath tp = (TreePath)(e.nextElement()); 178 if (!isVisible(tp)) // if we can't see it, we don't care 179 continue; 180 if (!isExpanded(tp)) // if it's not expanded, we don't need it 181 continue; 182 183 paths = DynamicArray.append(paths, tp); 184 } // while 185 186 return paths; 187 } 188 189 /** 190 * Restore open paths in the tree. Should be called on the event thread. 191 * @param openUrls The path urls to restore. 192 * @param queue True if the request should be put into the event queue at the 193 * end - this can be important for proper queuing of operations. 194 */ 195 void restorePaths(final String[] openUrls, boolean queue) { 196 if (openUrls == null || openUrls.length == 0) 197 return; 198 199 if (currModel instanceof TestTreeModel) { 200 final TreePath[] paths = currModel.urlsToPaths(openUrls); 201 202 if (!EventQueue.isDispatchThread() || queue) { 203 EventQueue.invokeLater(new Runnable() { 204 public void run() { 205 restorePaths(paths); 206 } 207 }); 208 } 209 else 210 restorePaths(paths); 211 } 212 } 213 214 /** 215 * Attempt to restore paths which were previously recorded. 216 * @see #snapshotOpenPaths 217 */ 218 void restorePaths(TreePath[] paths) { 219 // make sure root still matches 220 if (paths == null || paths.length == 0) 221 return; 222 223 for (int i = 0; i < paths.length; i++) { 224 if (paths[i].getPathCount() == 1) 225 continue; 226 227 //expandPath(paths[i]); 228 setExpandedState(paths[i], true); 229 makeVisible(paths[i]); 230 } // for 231 } 232 233 private UIFactory uif; 234 private TestTreeModel currModel; 235 private TreePanelModel tpm; 236 private FilterSelectionHandler filterHandler; 237 private EventWatcher watcher = new EventWatcher(); 238 239 //protected static boolean debug = boolean.getboolean("debug." + testtree.class.getname()); 240 private static int debug = Debug.getInt("debug." + TestTree.class.getName()); 241 242 private class EventWatcher implements TreeModelListener, FilterSelectionHandler.Observer { 243 // TreeModelListener - on event thread 244 public void treeNodesChanged(TreeModelEvent e) { 245 // NOTE: cannot get selected item from tree because if it has 246 // been replaced already, there may be nothing selected 247 String activeTest = tpm.getSelectedTest(); 248 if (activeTest == null) { 249 return; 250 } 251 252 Object[] targets = e.getChildren(); 253 if (targets == null) 254 return; // don't care then 255 256 for (int i = 0; i < targets.length; i++) 257 if (targets[i] instanceof TT_TestNode) { 258 TestResult tr = ((TT_TestNode)targets[i]).getTestResult(); 259 260 if (tr.getTestName().equals(activeTest)) 261 tpm.showTest(tr); 262 } 263 264 repaint(); 265 } 266 267 public void treeNodesInserted(TreeModelEvent e) { 268 // NOTE: cannot get selected item from tree because if it has 269 // been replaced already, there may be nothing selected 270 String activeTest = tpm.getSelectedTest(); 271 if (activeTest == null) { 272 return; 273 } 274 275 Object[] targets = e.getChildren(); 276 if (targets == null) 277 return; // don't care then 278 279 for (int i = 0; i < targets.length; i++) 280 if (targets[i] instanceof TT_TestNode) { 281 TestResult tr = ((TT_TestNode)targets[i]).getTestResult(); 282 283 if (tr.getTestName().equals(activeTest)) 284 tpm.showTest(tr); 285 } 286 } 287 288 public void treeNodesRemoved(TreeModelEvent e) { 289 // XXX need to handle case when shown node is removed 290 // select root in that case? 291 } 292 293 public void treeStructureChanged(TreeModelEvent e) { 294 TreePath[] tp = snapshotOpenPaths(); 295 296 TreePath[] selected = snapshotSelectedPaths(); 297 298 //tpm.showNode(getModel().getRoot(), new TreePath(getModel().getRoot())); 299 300 if (tp != null && tp.length != 0) 301 restorePaths(tp); 302 303 restoreSelection(selected); 304 } 305 306 // FilterConfig.Observer - may not be on event thread 307 public void filterUpdated(TestFilter f) { 308 updateGUI(); 309 } 310 311 public void filterSelected(TestFilter f) { 312 updateGUI(); 313 } 314 315 public void filterAdded(TestFilter f) { 316 // don't care 317 } 318 319 public void filterRemoved(TestFilter f) { 320 // don't care 321 } 322 323 } 324 }