1 /*
   2  * $Id$
   3  *
   4  * Copyright (c) 2001, 2018, 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.BorderLayout;
  30 import java.awt.Color;
  31 import java.awt.Dimension;
  32 import java.awt.EventQueue;
  33 import java.awt.Font;
  34 import java.awt.Rectangle;
  35 import java.awt.event.ActionEvent;
  36 import java.awt.event.ActionListener;
  37 import java.awt.event.InputEvent;
  38 import java.awt.event.KeyEvent;
  39 import java.awt.event.MouseAdapter;
  40 import java.awt.event.MouseEvent;
  41 import java.util.ArrayList;
  42 import java.util.Hashtable;
  43 import java.util.List;
  44 import java.util.Map;
  45 import java.util.Vector;
  46 
  47 import javax.swing.AbstractAction;
  48 import javax.swing.Action;
  49 import javax.swing.DefaultListModel;
  50 import javax.swing.JComponent;
  51 import javax.swing.JDialog;
  52 import javax.swing.JList;
  53 import javax.swing.JMenu;
  54 import javax.swing.JMenuItem;
  55 import javax.swing.JOptionPane;
  56 import javax.swing.JPanel;
  57 import javax.swing.JPopupMenu;
  58 import javax.swing.JScrollPane;
  59 import javax.swing.JSplitPane;
  60 import javax.swing.JTextArea;
  61 import javax.swing.JTextField;
  62 import javax.swing.KeyStroke;
  63 import javax.swing.ListModel;
  64 import javax.swing.MenuElement;
  65 import javax.swing.ScrollPaneConstants;
  66 import javax.swing.Timer;
  67 import javax.swing.event.*;
  68 import javax.swing.tree.TreeModel;
  69 import javax.swing.tree.TreeSelectionModel;
  70 import javax.swing.tree.TreePath;
  71 
  72 import com.sun.javatest.tool.Deck;
  73 import com.sun.javatest.tool.UIFactory;
  74 
  75 import com.sun.javatest.exec.Session.Event;
  76 import com.sun.javatest.Harness;
  77 import com.sun.javatest.JavaTestError;
  78 import com.sun.javatest.Parameters;
  79 import com.sun.javatest.TestResult;
  80 import com.sun.javatest.TestResultTable;
  81 import com.sun.javatest.TestResultTable.TreeNode;
  82 import com.sun.javatest.TestSuite;
  83 import com.sun.javatest.WorkDirectory;
  84 import com.sun.javatest.tool.Preferences;
  85 import com.sun.javatest.util.Debug;
  86 import com.sun.javatest.util.DynamicArray;
  87 import com.sun.javatest.util.I18NResourceBundle;
  88 import com.sun.javatest.util.StringArray;
  89 
  90 /**
  91  * This panel is a split panel which has the testsuite tree on the left and a context
  92  * sensitive panel on the right.
  93  */
  94 class TestTreePanel extends JPanel implements ET_TestTreeControl, HarnessAware, Session.Observer {
  95 
  96     /**
  97      * Constructs TestTreePanel object, doesn't initialize GUI!
  98      */
  99     public TestTreePanel(JComponent parent, ExecModel em, UIFactory uif) {
 100         this.uif = uif;
 101         this.execModel = em;
 102         this.parent = parent;
 103         uif.setAccessibleInfo(this, "treep");
 104     }
 105     /**
 106      * @param map Saved state map, may be null.
 107      */
 108     public TestTreePanel(UIFactory uif, Harness h, ExecModel em,
 109             FilterSelectionHandler fh, JComponent parent,
 110             Map<String, String> map) {
 111         this(parent, em, uif);
 112         setHarness(h);
 113         this.filterHandler = fh;
 114         this.stateMap = map;
 115         initialize();
 116     }
 117 
 118     public void initialize() {
 119         initGUI();
 120     }
 121 
 122     /**
 123      * Returns the current TRT, or null if no table is available.
 124      */
 125     public synchronized TestResultTable getTestResultTable() {
 126         /*
 127         WorkDirectory wd = params.getWorkDirectory();
 128         if (wd == null)
 129         return null;
 130         else
 131         return wd.getTestResultTable();
 132          */
 133         return initialized ? treeModel.getTestResultTable() : null;
 134     }
 135 
 136     /**
 137      * Discover which parameters this panel is using.
 138      */
 139     public Parameters getParameters() {
 140         return params;
 141     }
 142 
 143     public synchronized TreePanelModel getTreePanelModel() {
 144         return pm;
 145     }
 146 
 147     public synchronized void dispose() {
 148         disposed = true;
 149 
 150         // zap cache worker thread
 151         if (treeModel != null) {
 152             treeModel.dispose();
 153             treeModel = null;
 154         }
 155 
 156         // zap counter thread
 157         if (brPanel != null) {
 158             brPanel.dispose();
 159             brPanel = null;
 160         }
 161 
 162         // zap bg workers for purge or refresh
 163         if (bgThread != null && bgThread.isAlive()) {
 164             bgThread.interrupt();
 165         }
 166         params = null;
 167         newParams = null;
 168         popup = null;
 169         lastPopupPath = null;
 170         testMenus = folderMenus = mixedMenus = customMenus = null;
 171     }
 172 
 173     // --------- private ---------
 174     /**
 175      * This method should only be called to indicate that a change has occurred
 176      * which replaces the active TRT.  In most cases, a call to this method
 177      * should be followed by on to updateGUI().  Changes to the parameters
 178      * (filters, initial URLs, etc... should propagate thru the FilterConfig
 179      * system.<br>
 180      * It does nothing but remembers passed object. The real work is done
 181      * by applyParameters() method.
 182      *
 183      *
 184      * @see #updateGUI
 185      * @see #applyParameters
 186      * @see com.sun.javatest.exec.FilterConfig
 187      */
 188     public synchronized void setParameters(Parameters p) {
 189         this.newParams = p;
 190         newConfigAlreadyApplied = false;
 191     }
 192 
 193     /**
 194      * Applies changed Parameters iff changed since last time.
 195      * Method to be invoked from updateGUI().
 196      */
 197     synchronized void applyParameters(boolean force) {
 198         if (!force) {
 199             if (newParams == null) {
 200                 return;
 201             }
 202             if (newConfigAlreadyApplied) {
 203                 return;
 204             }
 205             newConfigAlreadyApplied = true;
 206         }
 207 
 208         String[] paths = null;
 209         String[] selectedPaths = null;
 210 
 211         // this is important because there are no params at construction
 212         // time, so we need to make sure to make use of the preferences
 213         if (params == null && stateMap != null) {
 214             // trying to retrieve previous state
 215             String tmp = stateMap.get(OPEN_PATHS_PREF);
 216             if (tmp != null) {
 217                 paths = StringArray.split(tmp);
 218             }
 219         } else {
 220             if (tree != null && !(treeModel instanceof EmptyTestTreeModel)) {
 221                 TreePath[] ps = tree.snapshotOpenPaths();
 222                 paths = treeModel.pathsToStrings(ps);
 223 
 224                 ps = tree.snapshotSelectedPaths();
 225                 selectedPaths = treeModel.pathsToStrings(ps);
 226             }
 227         }
 228 
 229         this.params = newParams;
 230 
 231         if (treeModel instanceof EmptyTestTreeModel) {
 232             WorkDirectory wd = execModel.getWorkDirectory();
 233             ContextManager cm = execModel.getContextManager();
 234             EmptyTestTreeModel em = (EmptyTestTreeModel) treeModel;
 235 
 236             if (wd != null && cm != null && cm.getFeatureManager().isEnabled(FeatureManager.NO_TREE_WITHOUT_WD)) {
 237                 cm.openTree(wd);
 238                 treeModel = new TestTreeModel(params, filterHandler, uif);
 239                 tree.setTreeModel(treeModel);
 240 
 241                 for (TreeModelListener l : em.getTreeModelListeners()) {
 242                     treeModel.addTreeModelListener(l);
 243                 }
 244                 for (TestResultTable.TreeNodeObserver o : em.getRootObservers()) {
 245                     ((TestResultTable.TreeNode) treeModel.getRoot()).addObserver(o);
 246                 }
 247                 harness.removeObserver(pm);
 248                 pm = new PanelModel();
 249                 harness.addObserver(pm);
 250                 brPanel.dispose();
 251                 brPanel.setTreeModel(treeModel);
 252 
 253                 em.dispose();
 254             }
 255         }
 256 
 257         if (treeModel != null) {
 258             treeModel.setParameters(params);
 259         }
 260 
 261         if (tree != null) {
 262             tree.setParameters(params);
 263             treeRend.setParameters(params);
 264         }
 265 
 266         if (testPanel != null) {
 267             testPanel.setTestSuite(params.getTestSuite());
 268         }
 269 
 270         if (brPanel != null) {
 271             brPanel.setParameters(params);
 272         }
 273 
 274         // restore onscreen open paths
 275         if (tree != null && paths != null && paths.length > 0) {
 276             tree.restorePaths(paths, true);
 277         }
 278 
 279         if (tree != null ) {
 280             tree.restoreSelection(selectedPaths);
 281         }
 282 
 283         if (tree != null) {
 284             ensureTreeSelection();
 285         }
 286 
 287     }
 288 
 289     private TreePath[] getTreePaths(String[] paths) {
 290         if (paths != null) {
 291             ArrayList<TreePath> translatedPaths = new ArrayList<>(paths.length);
 292 
 293             for (int i = 0; i < paths.length; i++) {
 294                 // the paths need to be reconditioned because the JTree will not
 295                 // accept them if they refer to a different model/TRT.
 296                 //Object targetNode = trt.resolveUrl(paths[i]);
 297 
 298                 TreePath tp = treeModel.resolveUrl(paths[i]);
 299                 if (tp != null) {
 300                     translatedPaths.add(tp);
 301                 }
 302             }   // for (i)
 303 
 304             TreePath[] result = new TreePath[translatedPaths.size()];
 305             translatedPaths.toArray(result);
 306             return result;
 307         } else {
 308             return null;
 309         }
 310     }
 311 
 312 
 313     @Override
 314     public void saveTreeState(Map<String, String> m) {
 315         String[] paths = getOpenPaths();
 316         m.put(OPEN_PATHS_PREF, StringArray.join(paths));
 317     }
 318 
 319     @Override
 320     public void restoreTreeState(Map<String, String> m) {
 321         if (m != null) {
 322             String tmp = m.get(OPEN_PATHS_PREF);
 323             if (tree != null && tmp != null && tmp.length() > 0) {
 324                 tree.restorePaths(StringArray.split(tmp), true);
 325             }
 326         }
 327     }
 328 
 329     @Override
 330     public void save(Map<String, String> m) {
 331         // save the open paths
 332         Preferences.access();
 333         saveTreeState(m);
 334     }
 335 
 336     @Override
 337     public void restore(Map<String, String> m) {
 338         stateMap = m;
 339         restoreTreeState(stateMap);
 340     }
 341 
 342     /**
 343      * Translates open tree paths into string URLs.
 344      * @return null if no paths are open or the tree is not available.
 345      */
 346     private String[] getOpenPaths() {
 347         if (tree == null || tree.getModel() instanceof EmptyTestTreeModel) {
 348             return null;
 349         }
 350 
 351         TreePath[] paths = tree.snapshotOpenPaths();
 352         Vector<String> urls = new Vector<>();
 353 
 354         if (paths != null) {
 355             for (int i = 0; i < paths.length; i++) {
 356                 Object last = paths[i].getLastPathComponent();
 357                 String url;
 358 
 359                 if (last instanceof TT_TestNode) {
 360                     // get url
 361                     url = ((TT_TestNode) last).getTestResult().getTestName();
 362                 }
 363                 else if (last instanceof TT_BasicNode ) {       // tree node
 364                     // get url
 365                     url = ((TT_BasicNode) last).getLongPath();
 366                 }
 367                 else
 368                     return null;
 369 
 370                 urls.addElement(url);
 371             }   // for
 372         }
 373 
 374         if (urls == null || urls.size() == 0) {
 375             return null;
 376         } else {
 377             String[] result = new String[urls.size()];
 378             urls.copyInto(result);
 379             return result;
 380         }
 381     }
 382 
 383     /* BK now unused
 384     private TreePath nodeToPath(Object node, TestResultTable trt) {
 385     if (node instanceof TestResult) {
 386     TestResult tr = (TestResult)node;
 387 
 388     }
 389     else {      // tree node
 390     // trans url to path
 391     Object[] ttp = TestResultTable.getObjectPath((TreeNode)node);
 392 
 393     if (ttp != null && ttp.length > 0)
 394     return new TreePath(ttp);
 395     else
 396     return null;
 397     }
 398     }
 399      */
 400     private void setPopupItemsEnabled(boolean state) {
 401         if (state) {
 402             // enable all if possible
 403             boolean haveWorkDir = (params != null && params.getWorkDirectory() != null);
 404             purgeMI.setEnabled(haveWorkDir);
 405             refreshMI.setEnabled(params != null && params.getTestSuite() != null);
 406 
 407             // maybe should be based on whether the interview is complete
 408             runMI.setEnabled(!execModel.isConfiguring());
 409         } else {
 410             // disable all
 411             purgeMI.setEnabled(false);
 412             refreshMI.setEnabled(false);
 413             runMI.setEnabled(false);
 414         }
 415     }
 416 
 417     private void clearNodes(final TreePath[] what) {
 418         final WorkDirectory wd = execModel.getWorkDirectory();
 419 
 420         if (wd == null) {
 421             JOptionPane.showMessageDialog(parent,
 422                     uif.getI18NString("treep.cantPurgeNoWd.msg"),
 423                     uif.getI18NString("treep.cantPurgeNoWd.title"),
 424                     JOptionPane.WARNING_MESSAGE);
 425             return;
 426         }
 427 
 428         if (harness.isRunning()) {
 429             JOptionPane.showMessageDialog(parent,
 430                     uif.getI18NString("treep.cantPurgeRunning.msg"),
 431                     uif.getI18NString("treep.cantPurgeRunning.title"),
 432                     JOptionPane.WARNING_MESSAGE);
 433             return;
 434         }
 435 
 436         boolean ack = false;
 437         Object[] toPurge = new Object[what.length];
 438 
 439         if (what.length > 1) {
 440             //int confirm = uif.showYesNoDialog("treep.purgeItemsSure",
 441             //createNodeListString(createNodeList(what)));
 442 
 443             String[] paths = createNodeList(what);
 444             DefaultListModel<String> model = new DefaultListModel<>();
 445             for (int i = paths.length; i > 0; i--) {
 446                 model.add(model.getSize(), paths[model.getSize()]);
 447             }
 448             int confirm = showConfirmListDialog("treep.purgeItemsSure", null, model);
 449 
 450             // user backs out
 451             if (confirm != JOptionPane.YES_OPTION) {
 452                 return;
 453             } else {
 454                 ack = true;
 455                 for (int i = 0; i < what.length; i++) {
 456                     Object item = what[i].getLastPathComponent();
 457                     if (item instanceof TT_TestNode) {
 458                         toPurge[i] = ((TT_TestNode) item).getTestResult().getWorkRelativePath();
 459                     } else if (item instanceof TT_BasicNode) {
 460                         TT_BasicNode tn = (TT_BasicNode) item;
 461                         if (tn.isRoot()) {
 462                             toPurge = new Object[1];
 463                             toPurge[0] = "";
 464                             break;      // no need to process the rest of the list
 465                         } else {
 466                             toPurge[i] = tn.getLongPath();
 467                         }
 468                     } else {
 469                     }
 470                 }   // for
 471             }
 472         } // just one node
 473         else if (what[0].getLastPathComponent() instanceof TT_TestNode) {
 474             TestResult tr = ((TT_TestNode) (what[0].getLastPathComponent())).getTestResult();
 475 
 476             int confirm = uif.showYesNoDialog("treep.purgeTestSure",
 477                     tr.getTestName());
 478 
 479             // user backs out
 480             if (confirm != JOptionPane.YES_OPTION) {
 481                 return;
 482             } else {
 483                 //wd.purge(tr.getWorkRelativePath());
 484                 ack = true;
 485                 toPurge[0] = tr.getWorkRelativePath();
 486             }
 487         } else {
 488             TT_BasicNode tn = (TT_BasicNode) (what[0].getLastPathComponent());
 489 
 490             int confirm = 0;
 491             if (tn.isRoot()) {
 492                 confirm = uif.showYesNoDialog("treep.purgeRootSure");
 493             } else {
 494                 confirm = uif.showYesNoDialog("treep.purgeNodeSure",
 495                         tn.getLongPath());            // user backs out
 496             }
 497             if (confirm != JOptionPane.YES_OPTION) {
 498                 return;
 499             } else {
 500                 ack = true;
 501                 toPurge[0] = tn;
 502             }
 503         }
 504 
 505         // only go in here if user confirmed the operation
 506         if (ack) {
 507             // this block is intended to do the following:
 508             // - disable menu items
 509             // - start purge on background thread
 510             // - show a wait dialog if the operation exceeds a min. time
 511             // - hide dialog and re-enable menu item when thread finishes
 512             final JDialog d = uif.createWaitDialog("treep.waitPurge", this);
 513             final String[] finalList = createNodeList(toPurge);
 514 
 515             // disable all menu items
 516             setPopupItemsEnabled(false);
 517 
 518             final Thread t = new Thread() {
 519 
 520                 @Override
 521                 public void run() {
 522                     for (int i = 0; i < what.length; i++) {
 523                         try {
 524                             // this may take a long while...
 525                             for (int j = 0; j < finalList.length; j++) {
 526                                 wd.purge(finalList[j]);
 527                             }
 528                         } // try
 529                         catch (WorkDirectory.PurgeFault f) {
 530                             // print something in log...
 531                             I18NResourceBundle i18n = uif.getI18NResourceBundle();
 532                             wd.log(i18n, "treep.purgeFail.err", f);
 533                         } // catch
 534                         finally {
 535                             // fixup GUI on GUI thread
 536                             try {
 537                                 EventQueue.invokeAndWait(new Runnable() {
 538 
 539                                     public void run() {
 540                                         if (d.isShowing()) {
 541                                             d.hide();
 542                                         // enable all menu items
 543                                         }
 544                                         setPopupItemsEnabled(true);
 545 
 546                                         // reselect tree nodes
 547                                         TreePath[] translatedPaths = getTreePaths(finalList);
 548                                         if (translatedPaths != null && tree != null) {
 549                                             tree.setSelectionPaths(translatedPaths);
 550                                         }
 551                                     }
 552                                 });
 553                             } catch (InterruptedException e) {
 554                             } catch (java.lang.reflect.InvocationTargetException e) {
 555                             }
 556                         }   // outer try
 557                     }   // for
 558                 }   // run()
 559             };  // thread
 560 
 561             ActionListener al = new ActionListener() {
 562 
 563                 public void actionPerformed(ActionEvent evt) {
 564                     // show dialog if still processing
 565                     if (t == null) {
 566                         return;
 567                     } else if (t.isAlive() && !d.isVisible()) {
 568                         d.show();
 569                     } else if (!t.isAlive() && d.isVisible()) {
 570                         // just in case...a watchdog type check
 571                         d.hide();
 572                     }
 573                 }
 574             };
 575 
 576             bgThread = t;
 577 
 578             // show wait dialog if operation is still running after
 579             // WAIT_DIALOG_DELAY
 580             Timer timer = new Timer(WAIT_DIALOG_DELAY, al);
 581             timer.setRepeats(false);
 582 
 583             // do it!
 584             // in this order to reduce race condition
 585             timer.start();
 586             t.start();
 587         }   // outer if
 588     }
 589 
 590     // XXX need to find a shared place for these two methods to live
 591     static String createNodeListString(String[] items) {
 592         StringBuffer sb = new StringBuffer();
 593 
 594         for (int i = 0; i < items.length; i++) {
 595             sb.append("      ");
 596             sb.append(items[i]);
 597 
 598             if (i + 1 < items.length) {
 599                 sb.append("\n");
 600             }
 601         }   // for
 602 
 603         return sb.toString();
 604     }
 605 
 606     static String[] createNodeList(Object[] items) {
 607         String[] result = new String[items.length];
 608 
 609         for (int i = 0; i < items.length; i++) {
 610             Object item = items[i];
 611 
 612             if (item instanceof TreePath) {
 613                 item = ((TreePath) item).getLastPathComponent();
 614             }
 615             if (item instanceof TT_TestNode) {
 616                 TestResult tr = ((TT_TestNode) item).getTestResult();
 617                 result[i] = tr.getTestName();
 618             } else if (item instanceof TT_BasicNode) {
 619                 if (((TT_BasicNode) item).isRoot()) {
 620                     result = new String[1];
 621                     result[0] = TestResultTable.getRootRelativePath(((TT_BasicNode)item).getTableNode());
 622                     return result;
 623                 } else {
 624                     TestResultTable.TreeNode tn = ((TT_BasicNode) item).getTableNode();
 625                     result[i] = TestResultTable.getRootRelativePath(tn);
 626                 }
 627             } else if (item instanceof TestResult) {
 628                 // not used anymore, but left in case we decide to repopulate
 629                 // the list with actual TestResult objects
 630                 TestResult tr = (TestResult) item;
 631                 result[i] = tr.getTestName();
 632             } else if (item instanceof TestResultTable.TreeNode) {
 633                 TestResultTable.TreeNode tn = (TestResultTable.TreeNode) item;
 634                 result[i] = TestResultTable.getRootRelativePath(tn);
 635             } else // should not happen
 636             if (items[i] != null) {
 637                 result[i] = items[i].toString();
 638             }
 639         }   // for
 640 
 641         return result;
 642     }
 643 
 644     private void showNodeInfoDialog(TreePath what) {
 645         if (what.getLastPathComponent() instanceof TT_TreeNode) {
 646             TT_TreeNode tn = (TT_TreeNode) (what.getLastPathComponent());
 647             Debug.println("info for this node not implemented" +
 648                     tn.getDisplayName() + " (" + tn + ")");
 649         }
 650     }
 651 
 652     private void runNodes(TreePath[] what) {
 653         if (harness.isRunning()) {
 654             JOptionPane.showMessageDialog(parent,
 655                     uif.getI18NString("treep.cantRunRunning.msg"),
 656                     uif.getI18NString("treep.cantRunRunning.title"),
 657                     JOptionPane.WARNING_MESSAGE);
 658             return;
 659         }
 660 
 661         execModel.runTests(createNodeList(what));
 662     }
 663 
 664     private static void restore(final TreePath[] paths, final TestTree targetTree) {
 665         if (paths == null || targetTree == null) {
 666             return;        // we do it this way so that the tree updates itself, THEN we
 667         // ask it to restore
 668         }
 669         Runnable restorer = new Runnable() {
 670 
 671             public void run() {
 672                 //targetTree.restorePaths(paths);
 673             }
 674         };      // Runnable
 675 
 676         EventQueue.invokeLater(restorer);
 677     }
 678 
 679     private void refreshNodes(TreePath[] what) {
 680        // dialog to confirm wipe of results with refresh?
 681 
 682         if (harness.isRunning()) {
 683             JOptionPane.showMessageDialog(parent,
 684                     uif.getI18NString("treep.cantRefreshRunning.msg"),
 685                     uif.getI18NString("treep.cantRefreshRunning.title"),
 686                     JOptionPane.WARNING_MESSAGE);
 687             return;
 688         }
 689 
 690         final TestResultTable trt = treeModel.getTestResultTable();
 691         boolean ack = false;
 692         String[] ackTargets = new String[what.length];
 693         TT_TreeNode[] ackNodes = null;
 694 
 695         if (what.length > 1) {
 696             //int confirm = uif.showYesNoDialog("treep.refreshNodeSure",
 697             //          createNodeListString(createNodeList(what)));
 698 
 699             String[] paths = createNodeList(what);
 700             DefaultListModel<String> model = new DefaultListModel<>();
 701             for (int i = paths.length; i > 0; i--) {
 702                 model.add(model.getSize(), paths[model.getSize()]);
 703             }
 704             int confirm = showConfirmListDialog("treep.refreshNodeSure", paths, model);
 705 
 706             if (confirm != JOptionPane.YES_OPTION) {
 707                 return;
 708             }
 709             else {
 710                 ack = true;
 711             }
 712 
 713             for (int i = 0; i < what.length; i++) {
 714                 Object item = what[i].getLastPathComponent();
 715                 if (item instanceof TT_TestNode) {
 716                     ackTargets[i] = ((TT_TestNode) item).getLongPath();
 717                     ackNodes = DynamicArray.append(ackNodes, (TT_TestNode)item, TT_TreeNode.class);
 718                 } else if (item instanceof TT_BasicNode) {
 719                     ackNodes = DynamicArray.append(ackNodes, (TT_BasicNode)item, TT_TreeNode.class);
 720                     TT_BasicNode tn = (TT_BasicNode) item;
 721                     if (tn.isRoot()) {
 722                         ackTargets = new String[1];
 723                         ackTargets[0] = null;
 724                         break;  // no other results needed
 725                     } else {
 726                         // does not happen
 727                         //ackTargets[i] = tn.getLongPath();
 728                     }
 729                 } else {
 730                 }
 731             }   // for
 732 
 733         } // BK deprecated, will never happen
 734         else if (what[0].getLastPathComponent() instanceof TestResult) {
 735             final TestResult tr = (TestResult) (what[0].getLastPathComponent());
 736 
 737             int confirm = uif.showYesNoDialog("treep.refreshTestSure",
 738                     tr.getTestName());
 739 
 740             // user backs out
 741             if (confirm != JOptionPane.YES_OPTION) {
 742                 return;
 743             }
 744             ack = true;
 745             ackTargets[0] = tr.getTestName();
 746         } // single node of any type is selected
 747         else {
 748             final TT_TreeNode tn = (TT_TreeNode) (what[0].getLastPathComponent());
 749             int confirm = JOptionPane.NO_OPTION;
 750             ackTargets[0] = tn.getLongPath();
 751             if (tn.isRoot()) {
 752                 confirm = uif.showYesNoDialog("treep.refreshRootSure");
 753             } else {
 754                 confirm = uif.showYesNoDialog("treep.refreshNodeSure",
 755                         ackTargets[0]);            // user backs out
 756             }
 757             if (confirm != JOptionPane.YES_OPTION) {
 758                 return;
 759             }
 760             else
 761                 ack = true;
 762 
 763             if (tn instanceof TT_BasicNode) {
 764                 ackNodes = new TT_TreeNode[] {tn};
 765             }
 766         }   // else
 767 
 768         // NOTES...
 769         // if one node being refreshed, ackNodes is null
 770         if (ack) {
 771             final JDialog d = uif.createWaitDialog("treep.waitRef", this);
 772             final String[] finalTargets = ackTargets;
 773             final TT_TreeNode[] finalNodes = ackNodes;
 774             TreePath[] tp = tree.snapshotOpenPaths();
 775             final String[] openUrls = treeModel.pathsToStrings(tp);
 776             final String[] selectedUrls = treeModel.pathsToStrings(tree.snapshotSelectedPaths());
 777 
 778 
 779             // disable all menu items
 780             setPopupItemsEnabled(false);
 781             final Thread t = new Thread() {
 782 
 783                 {
 784                     setName("Tree refresh");
 785                 }
 786 
 787                 public void run() {
 788                     if (trt == null) {
 789                         return; // empty tree model set
 790                     }
 791                     boolean changes = false;
 792                     try {
 793                         // avoid deadlocking while other things are going on
 794                         trt.waitUntilReady();
 795                         treeModel.pauseWork();
 796 
 797                         if (finalNodes == null)
 798                             for (int i = 0; i < finalTargets.length; i++) {
 799                                 try {
 800                                     // this may take a long while...
 801                                      if (finalTargets[i] instanceof String) {
 802                                         changes = trt.refreshIfNeeded(finalTargets[i]);
 803                                     }
 804                                 } // try
 805                                 catch (TestResultTable.Fault f) {
 806                                     // log the error
 807                                     final WorkDirectory wd = execModel.getWorkDirectory();
 808                                     if (wd != null) {
 809                                         I18NResourceBundle i18n = uif.getI18NResourceBundle();
 810                                         wd.log(i18n, "treep.refFail",
 811                                                 new String[]{
 812                                                     finalTargets[i],
 813                                                     f.getMessage()
 814                                                 });
 815                                     }
 816                                 }       // catch
 817                             }   // for
 818                         else {
 819                             for (int i = 0; i < finalNodes.length; i++) {
 820                                 try {
 821                                     if (finalNodes[i] instanceof TT_TestNode)
 822                                         changes = trt.refreshIfNeeded(finalNodes[i].getLongPath());
 823                                     else {
 824                                         changes = trt.refreshIfNeeded(((TT_BasicNode)finalNodes[i]).getTableNode());
 825                                         if (changes) trt.prune(((TT_BasicNode)finalNodes[i]).getTableNode());
 826                                     }
 827 
 828                                 }
 829                                 catch (TestResultTable.Fault f) {
 830                                     // log the error
 831                                     final WorkDirectory wd = execModel.getWorkDirectory();
 832                                     if (wd != null) {
 833                                         I18NResourceBundle i18n = uif.getI18NResourceBundle();
 834                                         wd.log(i18n, "treep.refFail",
 835                                                 new String[]{
 836                                                     finalTargets[i],
 837                                                     f.getMessage()
 838                                                 });
 839                                     }
 840                                 }       // catch
 841                                 }   // for
 842                         }
 843                     } finally {
 844                         treeModel.unpauseWork();
 845                         final boolean updateTree = changes;
 846                         // fixup GUI on GUI thread
 847                         try {
 848                             EventQueue.invokeAndWait(new Runnable() {
 849 
 850                                 public void run() {
 851                                     if (d.isShowing()) {
 852                                         d.hide();
 853                                     // enable all menu items
 854                                     }
 855                                     setPopupItemsEnabled(true);
 856                                     if (updateTree) {
 857                                         restoreOpenTreePaths(openUrls);
 858                                         tree.restoreSelection(selectedUrls);
 859                                     }
 860                                 }
 861                             });
 862                         } catch (InterruptedException e) {
 863                         } catch (java.lang.reflect.InvocationTargetException e) {
 864                         }   // catch
 865                     }   // finally
 866                 }   // run()
 867             };  // thread
 868 
 869             ActionListener al = new ActionListener() {
 870 
 871                 public void actionPerformed(ActionEvent evt) {
 872                     // show dialog if still processing
 873                     if (t == null) {
 874                         return;
 875                     } else if (t.isAlive() && !d.isVisible()) {
 876                         d.show();
 877                     } else if (!t.isAlive() && d.isVisible()) {
 878                         // just in case...a watchdog type check
 879                         d.hide();
 880                     }
 881                 }
 882             };
 883 
 884             bgThread = t;
 885 
 886             // show wait dialog if operation is still running after
 887             // WAIT_DIALOG_DELAY
 888             Timer timer = new Timer(WAIT_DIALOG_DELAY, al);
 889             timer.setRepeats(true);
 890 
 891             // do it!
 892             // in this order to reduce race condition
 893             timer.start();
 894             t.start();
 895         }   // outer if
 896     }
 897 
 898     private void refreshFilters() {
 899         if (execModel instanceof ExecTool) {
 900             ExecTool et = (ExecTool)execModel;
 901             et.filterHandler.updateFilters();
 902         }
 903     }
 904 
 905     private void restoreOpenTreePaths(final String[] urls) {
 906         //treeModel = (TestTreeModel)(tree.getModel());
 907         //treeModel.notifyFullStructure();
 908         //tree.restorePaths(treeModel.urlsToPaths(openUrls));
 909         tree.restorePaths(urls, true);
 910     }
 911 
 912     /**
 913      * Call when it is likely that the Parameters have changed internally.
 914      * A call to this method may should be made if setParameters() is called.
 915      * This method should not be used to force updates when the filters have
 916      * changed, that should be done through the View mechanism.
 917      *
 918      * @see #setParameters
 919      */
 920     public synchronized void updateGUI() {
 921         if (debug) {
 922             Debug.println("TTP.updateGUI()");
 923         }
 924         if (!initialized) {
 925             return;
 926         }
 927         if (disposed) {
 928             return;        // enabled/disable popup menu items based on whether we have a
 929         // workdir or testsuite
 930         }
 931         applyParameters(false);
 932         if (popup != null && params != null) {
 933             MenuElement[] elems = popup.getSubElements();
 934             if (elems != null) {
 935                 boolean haveWorkDir = (params.getWorkDirectory() != null);
 936                 purgeMI.setEnabled(haveWorkDir);
 937                 refreshMI.setEnabled(params.getTestSuite() != null);
 938 
 939                 // maybe should be based on whether the interview is complete
 940                 //runMI.setEnabled(haveWorkDir && params.isValid());
 941                 runMI.setEnabled(!execModel.isConfiguring());
 942             } else {
 943             }
 944         } else {
 945         }
 946         tree.updateGUI();
 947         brPanel.updateGUI();
 948         startTreeUpdate();
 949     }
 950 
 951     private synchronized void startTreeUpdate() {
 952         final TestResultTable trt = getTestResultTable();
 953         if (trt != null && pm != null) {
 954             trt.addObserver(pm);
 955         }
 956         if (trt != null) {
 957             EventQueue.invokeLater(new Runnable() {
 958 
 959                 public void run() {
 960                     new Thread("Test tree updater") {
 961 
 962                         @Override
 963                         public void run() {
 964                             if (trt != null && trt.getWorkDirectory() != null
 965                                     && treeModel != null && !disposed) {
 966                                 try {
 967                                     treeModel.pauseWork();
 968                                     trt.getLock().lock();
 969                                     trt.waitUntilReady();
 970                                     trt.refreshIfNeeded(trt.getRoot());
 971 
 972                                     TT_BasicNode root = (TT_BasicNode) (treeModel.getRoot());
 973                                     for (int i = 0; i >= 0 && i < root.getChildCount(); i++) {
 974                                         Object c = root.getChildAt(i);
 975                                         if (c instanceof TT_BasicNode) {
 976                                             TT_BasicNode tn = (TT_BasicNode) c;
 977                                             if (tn.getChildCount() == 0) {
 978                                                 trt.prune(tn.getTableNode());
 979                                                 i--;
 980                                             }
 981                                         }
 982                                     }
 983                                     String[] openPaths = null;
 984                                     String[] selectedPaths = null;
 985                                     if (tree != null) {
 986                                         TreePath[] p = tree.snapshotOpenPaths();
 987                                         openPaths = treeModel.pathsToStrings(p);
 988 
 989                                         selectedPaths = treeModel.pathsToStrings(tree.snapshotSelectedPaths());
 990 
 991                                     }
 992 
 993                                     if (pm != null) {
 994                                         pm.refreshTree();
 995                                     }
 996 
 997                                     if (tree != null) {
 998                                         tree.invalidate();
 999 
1000                                         if (openPaths != null && openPaths.length > 0)
1001                                             tree.restorePaths(openPaths, true);
1002 
1003                                         tree.restoreSelection(selectedPaths);
1004 
1005                                     }
1006                                 } catch (TestResultTable.Fault f) {
1007                                 } finally {
1008                                     trt.getLock().unlock();
1009 
1010                                     if (!disposed)
1011                                         treeModel.unpauseWork();
1012                                 }
1013                             }
1014                         }
1015                     }.start();
1016                 }
1017             });
1018         }
1019     }
1020 
1021     private synchronized void initGUI() {
1022         if (initialized) {
1023             return;
1024         }
1025         initialized = true;
1026         setName("treeAndDetails");
1027 
1028         JSplitPane splitPane = uif.createSplitPane(JSplitPane.HORIZONTAL_SPLIT);
1029         listener = new Listener();
1030         // null params is okay
1031 
1032         WorkDirectory wd = execModel.getWorkDirectory();
1033         ContextManager cm = execModel.getContextManager();
1034         if (wd == null && cm != null && cm.getFeatureManager().isEnabled(FeatureManager.NO_TREE_WITHOUT_WD)) {
1035             cm.openTree(null);
1036             treeModel = new EmptyTestTreeModel(params, filterHandler, uif);
1037         } else {
1038             cm.openTree(wd);
1039             treeModel = new TestTreeModel(params, filterHandler, uif);
1040         }
1041         treeRend = new TT_Renderer(uif, filterHandler, pm);
1042 
1043         tree = new TestTree(uif, pm, filterHandler, treeModel);
1044         tree.setTreeModel(treeModel);
1045         tree.setCellRenderer(treeRend);
1046         tree.addTreeSelectionListener(listener);
1047         tree.addTreeExpansionListener(listener);
1048         tree.addMouseListener(listener);
1049         tree.getSelectionModel().setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
1050         tree.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
1051                 KeyStroke.getKeyStroke(KeyEvent.VK_F10,
1052                 InputEvent.SHIFT_MASK,
1053                 false),
1054                 "triggerPopup");
1055         tree.getActionMap().put("triggerPopup",
1056                 new TreePopupAction(uif.getI18NResourceBundle(), "treep.popup"));
1057         uif.setAccessibleInfo(tree, "treep.tree");
1058 
1059         JPanel left = uif.createPanel("tree", new BorderLayout(), false);
1060 
1061         left.add(uif.createScrollPane(tree, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
1062                 JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED),
1063                 BorderLayout.CENTER);
1064         splitPane.setLeftComponent(left);
1065 
1066         deck = new Deck() {
1067 
1068             public Dimension getPreferredSize() {
1069                 int dpi = uif.getDotsPerInch();
1070                 return new Dimension(6 * dpi, 4 * dpi);
1071             }
1072         };
1073 
1074         testPanel = new TestPanel(uif, harness, execModel.getContextManager());
1075         brPanel = new BranchPanel(uif, pm, harness, execModel, parent, filterHandler, treeModel);
1076         msPanel = new MultiSelectPanel(uif, pm, treeModel);
1077         deck.add(testPanel);
1078         deck.add(brPanel);
1079         deck.add(msPanel);
1080 
1081         deckPanel = uif.createPanel("main", false);
1082         deckPanel.setLayout(new BorderLayout());
1083         deckPanel.add(deck, BorderLayout.CENTER);
1084 
1085         // title strip, above the tabs, not above the tree
1086         titleField = uif.createOutputField("treep.title");
1087         titleField.setBorder(null);
1088         titleField.setText(uif.getI18NString("treep.title.noSelection.txt"));
1089         titleField.setBackground(UIFactory.Colors.MENU_BACKGROUND.getValue());
1090         //titleField.setForeground(MetalLookAndFeel.getUserTextColor());
1091         titleField.setForeground(new Color(102, 102, 102));   // #666666
1092         Font f = UIFactory.getBaseFont();
1093         titleField.setFont(new Font("Ariel", Font.BOLD, f.getSize()));
1094         titleField.setEnabled(true);
1095         titleField.setEditable(false);
1096 
1097         deckPanel.add(titleField, BorderLayout.NORTH);
1098 
1099         splitPane.setRightComponent(deckPanel);
1100         testPanel.setVisible(false);
1101         brPanel.setVisible(false);
1102         deck.setVisible(true);
1103         deckPanel.setVisible(true);
1104         splitPane.setDividerLocation(0.3);
1105         splitPane.setResizeWeight(0.3);     // 30% to left tree pane
1106 
1107         ensureTreeSelection();
1108 
1109         popup = uif.createPopupMenu("treep.popup.mnu");
1110         popup.add(runMI = uif.createMenuItem("treep.popup", "run", listener));
1111         popup.addSeparator();
1112         popup.add(refreshMI = uif.createMenuItem("treep.popup", "refresh", listener));
1113         popup.add(purgeMI = uif.createMenuItem("treep.popup", "clear", listener));
1114 
1115         // get items from the test suite
1116         if (cm != null) {
1117             JavaTestContextMenu[] cms = cm.getContextMenus();
1118             if (cms != null) {
1119                 popup.addSeparator();
1120 
1121                 for (int i = 0; i < cms.length; i++) {
1122                     // filter out types of menus we don't want (not needed yet)
1123                     // keep track of them for later
1124                     switch (cms[i].getMenuApplication()) {
1125                         case JavaTestContextMenu.TESTS_AND_FOLDERS:
1126                             if (mixedMenus == null) {
1127                                 mixedMenus = new ArrayList<>();
1128                             }
1129                             mixedMenus.add(cms[i]);
1130                             break;
1131                         case JavaTestContextMenu.TESTS_ONLY:
1132                             if (testMenus == null) {
1133                                 testMenus = new ArrayList<>();
1134                             }
1135                             testMenus.add(cms[i]);
1136                             break;
1137                         case JavaTestContextMenu.FOLDERS_ONLY:
1138                             if (folderMenus == null) {
1139                                 folderMenus = new ArrayList<>();
1140                             }
1141                             folderMenus.add(cms[i]);
1142                             break;
1143                         case JavaTestContextMenu.CUSTOM:
1144                             if (customMenus == null) {
1145                                 customMenus = new ArrayList<>();
1146                             }
1147                             customMenus.add(cms[i]);
1148                             break;
1149                         default:
1150                     }
1151                     popup.add(cms[i].getMenu());
1152                 }   // for
1153             }
1154         }
1155 
1156         // disabled by default
1157         // will immediately be reevaluated when setParameters() is called
1158         runMI.setEnabled(false);
1159         refreshMI.setEnabled(false);
1160         purgeMI.setEnabled(false);
1161 
1162         // setup top toolbar
1163         /*
1164         JPanel topPanel = uif.createPanel("treeTb", new GridBagLayout(), false);
1165         // NOTE intentional space added below to defeat
1166         //  i18n check in build while this is commented out
1167         JLabel lab = uif.createLabel ("treep.filter", true);
1168 
1169         JComponent selector = filterHandler.getFilterSelector();
1170         lab.setLabelFor(selector);
1171 
1172         GridBagConstraints gbc = new GridBagConstraints();
1173         gbc.gridy = 0;
1174         gbc.ipadx = 12;             // JL&F
1175         gbc.fill = GridBagConstraints.HORIZONTAL;
1176         gbc.anchor = GridBagConstraints.CENTER;
1177         gbc.gridwidth = GridBagConstraints.REMAINDER;
1178 
1179         gbc.gridy = 1;
1180         gbc.gridwidth = 1;
1181         gbc.fill = GridBagConstraints.NONE;
1182         gbc.anchor = GridBagConstraints.WEST;
1183 
1184         topPanel.add(lab, gbc);
1185         topPanel.add(selector, gbc);
1186 
1187         topPanel.add(uif.createHorizontalStrut(15), gbc);
1188 
1189         gbc.anchor = GridBagConstraints.EAST;
1190         gbc.fill = GridBagConstraints.BOTH;
1191         gbc.weightx = 3;
1192         topPanel.add(uif.createMessageArea("treep.filter"), gbc);
1193          */
1194 
1195         setLayout(new BorderLayout());
1196 
1197         add(splitPane, BorderLayout.CENTER);
1198     //add(topPanel, BorderLayout.NORTH);
1199         applyParameters(false);
1200         TestResultTable trt = treeModel.getTestResultTable();
1201         if (wd != null && trt != null) {
1202             try {
1203                 wd.setTestResultTable(trt);
1204             } catch (Exception ignore) {
1205             }
1206         }
1207 
1208         Preferences.access().addObserver("javatest.executionOrder", new Preferences.Observer() {
1209             @Override
1210             public void updated(String name, String newValue) {
1211 
1212                 treeModel.getTestResultTable().updateTestExecutionOrderOnTheFly();
1213 
1214                 try {
1215                     WorkDirectory wd = execModel.getWorkDirectory();
1216                     TestResultTable trt = getTestResultTable();
1217                     if(trt != null && !wd.isTRTSet()) {
1218                         wd.setTestResultTable(trt);
1219                     }
1220                     applyParameters(true);
1221                 } catch (Exception ee) {
1222                     ee.printStackTrace();
1223                 }
1224 
1225             }
1226         });
1227     }
1228 
1229     private void ensureTreeSelection() {
1230         // make sure some node is always selected
1231         if (tree.getSelectionPath() == null) {
1232             TreeModel tm = tree.getModel();
1233             try {
1234                 TT_BasicNode root = (TT_BasicNode) (tm.getRoot());
1235                 //selectBranch(root, new TreePath(root));
1236                 tree.setRootVisible(true);
1237                 EventQueue.invokeLater(new Selector(root, new TreePath(root)));
1238             } catch (ClassCastException e) {
1239                 // shouldn't happen really
1240                 if (debug) {
1241                     e.printStackTrace(Debug.getWriter());
1242                 }
1243             }
1244         }
1245     }
1246 
1247     private void updatePopupMenu() {
1248         TreePath[] paths = tree.getSelectionPaths();
1249 
1250         // neither of these should be possible, since the tree should
1251         // always have something selected
1252         if (paths == null || paths.length == 0) {
1253             return;
1254         }
1255 
1256         if (paths.length == 1) {
1257             Object item = paths[0];
1258 
1259             if (item instanceof TreePath) {
1260                 item = ((TreePath) item).getLastPathComponent();
1261             // determine leaf type
1262             }
1263             if (item instanceof TT_TestNode) {
1264                 TestResult tr = ((TT_TestNode) item).getTestResult();
1265                 if (testMenus != null) {
1266                     for (int i = 0; i < testMenus.size(); i++) {
1267                         testMenus.get(i).getMenu().setEnabled(true);
1268                         testMenus.get(i).updateState(tr);
1269                     }   // for
1270                 }
1271                 if (customMenus != null) {
1272                     for (int i = 0; i < customMenus.size(); i++) {
1273                         customMenus.get(i).updateState(tr);
1274                     }   // for
1275                 }
1276                 if (mixedMenus != null) {
1277                     for (int i = 0; i < mixedMenus.size(); i++) {
1278                         mixedMenus.get(i).getMenu().setEnabled(true);
1279                         mixedMenus.get(i).updateState(tr);
1280                     }   // for
1281                 }
1282                 if (folderMenus != null) {
1283                     for (int i = 0; i < folderMenus.size(); i++) {
1284                         folderMenus.get(i).getMenu().setEnabled(false);
1285                     }   // for
1286                 }
1287             } else if (item instanceof TT_BasicNode) {
1288                 String url = ((TT_BasicNode) item).getLongPath();
1289                 if (url == null)
1290                     url = "";   // this is just how we define it
1291 
1292                 if (testMenus != null) {
1293                     for (int i = 0; i < testMenus.size(); i++) {
1294                         testMenus.get(i).getMenu().setEnabled(false);
1295                     }   // for
1296                 }
1297                 if (customMenus != null) {
1298                     for (int i = 0; i < customMenus.size(); i++) {
1299                         customMenus.get(i).updateState(url);
1300                     }   // for
1301                 }
1302                 if (mixedMenus != null) {
1303                     for (int i = 0; i < mixedMenus.size(); i++) {
1304                         mixedMenus.get(i).getMenu().setEnabled(true);
1305                         mixedMenus.get(i).updateState(url);
1306                     }   // for
1307                 }
1308                 if (folderMenus != null) {
1309                     for (int i = 0; i < folderMenus.size(); i++) {
1310                         folderMenus.get(i).getMenu().setEnabled(true);
1311                         folderMenus.get(i).updateState(url);
1312                     }   // for
1313                 }
1314             } else {    // should not happen!
1315                 if (testMenus != null) {
1316                     for (int i = 0; i < testMenus.size(); i++) {
1317                         testMenus.get(i).getMenu().setEnabled(false);
1318                     }   // for
1319                 }
1320                 if (customMenus != null) {
1321                     for (int i = 0; i < customMenus.size(); i++) {
1322                         customMenus.get(i).getMenu().setEnabled(false);
1323                     }   // for
1324                 }
1325                 if (mixedMenus != null) {
1326                     for (int i = 0; i < mixedMenus.size(); i++) {
1327                         mixedMenus.get(i).getMenu().setEnabled(false);
1328                     }   // for
1329                 }
1330                 if (folderMenus != null) {
1331                     for (int i = 0; i < folderMenus.size(); i++) {
1332                         folderMenus.get(i).getMenu().setEnabled(false);
1333                     }   // for
1334                 }
1335 
1336                 if (!(tree.getModel() instanceof EmptyTestTreeModel))
1337                     throw new JavaTestError("Unknown node type from JTree!");
1338             }
1339         } else {      // multiple nodes selected
1340             ArrayList<TestResult> tests = new ArrayList<>();
1341             ArrayList<String> folders = new ArrayList<>();
1342 
1343             for (int i = 0; i < paths.length; i++) {
1344                 Object item = paths[i];
1345 
1346                 if (item instanceof TreePath) {
1347                     item = ((TreePath) item).getLastPathComponent();
1348                 }
1349 
1350                 if (item instanceof TT_TestNode) {
1351                     tests.add(((TT_TestNode) item).getTestResult());
1352                 } else {
1353                     TT_BasicNode bn = (TT_BasicNode)item;
1354                     if (bn.isRoot())
1355                         folders.add("");
1356                     else
1357                         folders.add(bn.getLongPath());
1358                 }
1359             }   // for
1360 
1361             TestResult[] t = null;
1362             if (tests.size() > 0) {
1363                 t = tests.toArray(new TestResult[tests.size()]);
1364             }
1365 
1366             String[] f = null;
1367             if (folders.size() > 0) {
1368                 f = folders.toArray(new String[folders.size()]);
1369                 // currently enable/disabled state is only determined by the menu's
1370                 // ability to deal with multi selection
1371             }
1372 
1373             if (testMenus != null) {
1374                 for (int i = 0; i < testMenus.size(); i++) {
1375                     JavaTestContextMenu m = testMenus.get(i);
1376                     m.getMenu().setEnabled(m.isMultiSelectAllowed());
1377                     m.updateState(f, t);
1378                 }   // for
1379             }
1380             if (customMenus != null) {
1381                 for (int i = 0; i < customMenus.size(); i++) {
1382                     JavaTestContextMenu m = customMenus.get(i);
1383                     m.getMenu().setEnabled(m.isMultiSelectAllowed());
1384                     m.updateState(f, t);
1385                 }   // for
1386             }
1387             if (mixedMenus != null) {
1388                 for (int i = 0; i < mixedMenus.size(); i++) {
1389                     JavaTestContextMenu m = mixedMenus.get(i);
1390                     m.getMenu().setEnabled(m.isMultiSelectAllowed());
1391                     m.updateState(f, t);
1392                 }   // for
1393             }
1394             if (folderMenus != null) {
1395                 for (int i = 0; i < folderMenus.size(); i++) {
1396                     JavaTestContextMenu m = folderMenus.get(i);
1397                     m.getMenu().setEnabled(m.isMultiSelectAllowed());
1398                     m.updateState(f, t);
1399                 }   // for
1400             }
1401         }
1402     }
1403 
1404 
1405     /**
1406      * This method should be called on the AWT event thread.
1407      */
1408     private void selectTest(TestResult tr, TreePath path) {
1409         if (debug) {
1410             Debug.println("TTP showing: " + tr);
1411             Debug.println("   -> path provided is " + path.getPathCount() + " components long.");
1412         }
1413 
1414         if (disposed) {
1415             if (debug) {
1416                 Debug.println("TTP - selectTest() not running, panel is disposed.");
1417             }
1418             return;
1419         }
1420 
1421         if (!isPopulated()) {
1422             if (debug) {
1423                 Debug.println("TTP - no data, cannot display a leaf. No action.");
1424             }
1425             return;
1426         }
1427 
1428         brPanel.setVisible(false);
1429         deck.show(testPanel);
1430 
1431         // translate into path for display
1432         // could reoptimize to use path param
1433         // otherwise path param is mostly deprecated
1434         TreePath jtPath = treeModel.resolveUrl(tr.getTestName());
1435         // setup the tree
1436         if (jtPath != null && !tree.isPathSelected(jtPath)) {
1437             tree.clearSelection();
1438             tree.setSelectionPath(jtPath);
1439         }
1440 
1441         if (!tree.isVisible(jtPath)) {
1442             tree.scrollPathToVisible(jtPath);        // setup the test panel
1443         }
1444 
1445         if (tr != testPanel.getTest()) {
1446             // select new node
1447             activeTest = tr.getTestName();
1448             // special case: if the selected test is running, the TR object in
1449             // the tree may not be the active one.  We want to see the running
1450             // test so we can provide real-time monitoring, so we check the
1451             // list of active TR objects.
1452             TestResult atr = pm.getActive(tr);
1453             testPanel.setTest((atr == null ? tr : atr));
1454         } else {
1455             // should we ask for a refresh if == ?
1456             // XXX could force an update here, which is needed
1457             // if test panel does not have dynamic update
1458             // no way to force as of 9/18/2002 though
1459             // this is a problem if the user selects a test while
1460             // it is in the running state, because the TR object
1461             // does not change, but the status of that object does
1462             // change
1463         }
1464 
1465         // configure the right side title
1466         titleField.setText(uif.getI18NString("treep.test", tr.getTestName()));
1467 
1468         testPanel.setVisible(true);
1469 
1470         tree.repaint();
1471         deckPanel.repaint();
1472     }
1473 
1474     /**
1475      * This method should be called on the AWT event thread.
1476      */
1477     private void selectBranch(TT_BasicNode tn, TreePath path) {
1478         if (debug) {
1479             Debug.println("TTP showing: " + tn.getShortName());
1480             Debug.println("   -> " + tn);
1481             Debug.println("   -> path provided is " + path.getPathCount() + " components long.");
1482         }
1483 
1484         if (disposed) {
1485             if (debug) {
1486                 Debug.println("TTP - selectBranch() not running, panel is disposed.");
1487             }
1488             return;
1489         }
1490 
1491         if (!isPopulated()) {
1492             if (debug) {
1493                 Debug.println("TTP - no data, cannot display a branch. No action.");
1494             }
1495             return;
1496         }
1497 
1498         // get rid of the test panel and show the branch panel
1499         if (deck.getCurrentCard() != brPanel) {
1500             deck.show(brPanel);
1501             testPanel.setVisible(false);
1502             activeTest = null;
1503         }
1504 
1505         // setup the tree
1506         if (!tree.isPathSelected(path)) {
1507             tree.clearSelection();
1508             tree.setSelectionPath(path);
1509         }
1510 
1511         if (!tree.isVisible(path)) {
1512             tree.scrollPathToVisible(path);        // setup the branch panel
1513         }
1514         if (tn != brPanel.getNode()) {
1515             // select new node
1516             brPanel.setNode(tn);
1517             treeModel.setActiveNode(tn);        // hint to cache
1518 
1519         } else {
1520             // should we ask for a refresh if == ?
1521         }
1522 
1523         // configure the right side title
1524         if (tn.isRoot()) {              // root node, has no name
1525             TestSuite ts = params.getTestSuite();
1526             String tsn = ts.getName();
1527             if (tsn != null) // use descriptive name of TestSuite
1528             {
1529                 titleField.setText(uif.getI18NString("treep.ts", tsn));
1530             } else // TestSuite has no name, use root path
1531             {
1532                 titleField.setText(uif.getI18NString("treep.ts", ts.getPath()));
1533             }
1534         } else {
1535             String nName = tn.getLongPath();
1536             titleField.setText(uif.getI18NString("treep.node", nName));
1537         }
1538 
1539         if (!brPanel.isVisible()) {
1540             brPanel.setVisible(true);
1541         }
1542 
1543         deckPanel.repaint();
1544         tree.repaint();
1545     }
1546 
1547     private void selectNodes(String[] paths) {
1548     }
1549 
1550     /**
1551      * Called when multiple nodes in the tree have been selected.
1552      * This method is invoked repeatably as the user adds more nodes to
1553      * the selection.
1554      */
1555     private void selectNodes(TreePath[] paths) {
1556         if (!isPopulated()) {
1557             if (debug) {
1558                 Debug.println("TTP - no data, cannot display selections. No action.");
1559             }
1560             return;
1561         }
1562 
1563         Object[] leaves = new Object[paths.length];
1564         for (int i = 0; i < paths.length; i++) {
1565             leaves[i] = paths[i].getLastPathComponent();
1566         }
1567         msPanel.setNodes(leaves);
1568 
1569         // get rid of the test panel and show the branch panel
1570         if (deck.getCurrentCard() != msPanel) {
1571             deck.show(msPanel);
1572             activeTest = null;
1573         }
1574 
1575         // configure the right side title
1576         titleField.setText(uif.getI18NString("treep.ms"));
1577 
1578         msPanel.setVisible(true);
1579         deckPanel.repaint();
1580     }
1581 
1582     /**
1583      * @param prefix i18n bundle prefix
1584      * @param args Arguments for the user message string, which is prefix.txt.
1585      */
1586     private int showConfirmListDialog(String prefix, Object[] args, ListModel<?> model) {
1587         // resources needed:
1588         // prefix.title
1589         JPanel p = uif.createPanel("ttp.confirmPanel", false);
1590         JTextArea msg = uif.createMessageArea(prefix, args);
1591         p.setLayout(new BorderLayout());
1592         p.add(msg, BorderLayout.NORTH);
1593 
1594         JList<?> list = uif.createList("treep.nodeList", model);
1595         p.add(uif.createScrollPane(list,
1596                 ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
1597                 ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED), BorderLayout.CENTER);
1598 
1599         return uif.showCustomYesNoDialog(prefix, p);
1600     }
1601 
1602     /**
1603      * Does this panel currently have data to work from.
1604      */
1605     private boolean isPopulated() {
1606         if (params == null || params.getTestSuite() == null) {
1607             return false;
1608         } else {
1609             return true;
1610         }
1611     }
1612     private UIFactory uif;
1613     private Harness harness;
1614     private FilterSelectionHandler filterHandler;
1615     private ExecModel execModel;
1616     private JComponent parent;
1617     private Thread bgThread;        // in case disposal is required
1618     private Map<String, String> stateMap;                   // startup state, read-only
1619     private volatile boolean disposed;
1620     private PanelModel pm;
1621     private TestTree tree;
1622     private TT_Renderer treeRend;
1623     private TestPanel testPanel;
1624     private BranchPanel brPanel;
1625     private MultiSelectPanel msPanel;
1626     private JPopupMenu popup;
1627     private TreePath lastPopupPath;
1628     private JMenuItem refreshMI;
1629     private JMenuItem purgeMI;
1630     private JMenuItem runMI;
1631     private String activeTest;
1632     private ArrayList<JavaTestContextMenu> testMenus,  folderMenus,  mixedMenus,  customMenus;
1633     private TestTreeModel treeModel;
1634     private Deck deck;
1635     private JPanel deckPanel;
1636     private JTextField titleField;
1637     private Listener listener;
1638     private TestSuite lastTs;       // last recorded testsuite
1639     private Parameters params;
1640     private Parameters newParams;
1641     private boolean newConfigAlreadyApplied = false;
1642     private boolean initialized = false;
1643     private static final int WAIT_DIALOG_DELAY = 3000;      // 3 second delay
1644     private static final String OPEN_PATHS_PREF = "openpaths";
1645     static protected boolean debug = Boolean.getBoolean("debug." + TestTreePanel.class.getName());
1646 
1647     /**
1648      * ET_Config method
1649      * @return null - no menu required
1650      */
1651     public JMenu getMenu() {
1652         return null;
1653     }
1654 
1655     /**
1656      * ET_Config method
1657      * @return null - no tool bar actions
1658      */
1659     public List<Action> getToolBarActionList() {
1660         return null;
1661     }
1662 
1663     public void setHarness(Harness h) {
1664         this.harness = h;
1665         if (harness != null && pm == null) {
1666             pm = new PanelModel();
1667             harness.addObserver(pm);
1668         }
1669     }
1670 
1671     /**
1672      * @return this
1673      */
1674     public JComponent getViewComponent() {
1675         return this;
1676     }
1677 
1678     public void setFilterSelectionHandler(FilterSelectionHandler fh) {
1679         this.filterHandler = fh;
1680     }
1681 
1682     /**
1683      * Session.Observer method. Invoked when parameters object has been changed.
1684      * @param ev
1685      */
1686     public void updated(Event ev) {
1687         if (ev instanceof BasicSession.E_NewConfig) {
1688             setParameters(((BasicSession.E_NewConfig)ev).ip);
1689         } else if (ev instanceof BasicSession.E_NewWD) {
1690             WorkDirectory wd = ((BasicSession.E_NewWD)ev).wd;
1691             try {
1692                 TestResultTable trt = getTestResultTable();
1693                 if(trt != null && !wd.isTRTSet()) {
1694                     wd.setTestResultTable(trt);
1695                 }
1696                 applyParameters(true);
1697             } catch (Exception ee) {
1698                 ee.printStackTrace();
1699             }
1700         } else if (ev instanceof BasicSessionControl.E_EditorVisibility) {
1701             this.runMI.setEnabled(!execModel.isConfiguring());
1702         }
1703     }
1704 
1705     private class Listener extends MouseAdapter
1706             implements ActionListener, TreeSelectionListener,
1707             TreeExpansionListener {
1708         // --- TreeSelectionListener ---
1709         public void valueChanged(TreeSelectionEvent e) {
1710             // make sure source is our tree
1711             // ignore the message if we are unselecting a path or if
1712             // this is a deletion event
1713             if (e.isAddedPath() && e.getSource() == tree) {
1714                 TreePath[] tp = e.getPaths();
1715                 TestTree source = (TestTree) (e.getSource());
1716                 dispatchSelection(source);
1717             }
1718         }
1719 
1720         // --- TreeExpansionListener ---
1721         // the path indicates the path to the node which contains the
1722         // now-(in)visible nodes
1723         public void treeCollapsed(TreeExpansionEvent event) {
1724             if (disposed) {
1725                 return;
1726             /*
1727             // send a hint to the model indicating that some nodes
1728             // are no longer visible
1729             TreePath tp = event.getPath();
1730             //Debug.println("collapsed " + ((TreeNode)(tp.getLastPathComponent())).getName());
1731             TreeNode tn = (TreeNode)(tp.getLastPathComponent());
1732 
1733             TreeNode[] childs = tn.getTreeNodes();
1734             if (childs != null)
1735             for (int i = 0; i < childs.length; i++)
1736             treeModel.removeRelevantNode(childs[i]);
1737 
1738             TestResult[] trs = tn.getTestResults();
1739             if (trs != null)
1740             for (int i = 0; i < trs.length; i++)
1741             treeModel.removeRelevantTest(trs[i]);
1742              */
1743             }
1744         }
1745 
1746         public void treeExpanded(TreeExpansionEvent event) {
1747             if (disposed || treeModel instanceof EmptyTestTreeModel) {
1748                 return;            // basically send a hint to the model indicating that some nodes
1749             // are now visible
1750             }
1751             TreePath tp = event.getPath();
1752             TT_BasicNode tn = (TT_BasicNode) (tp.getLastPathComponent());
1753             //Debug.println("expanded " + ((TreeNode)(tp.getLastPathComponent())).getName());
1754 
1755             for (int i = 0; i < tn.getChildCount(); i++) {
1756                 treeModel.addRelevantNode((TT_TreeNode) (tn.getChildAt(i)));
1757             /*
1758             TestResult[] trs = tn.getTestResults();
1759             if (trs != null)
1760             for (int i = 0; i < trs.length; i++)
1761             treeModel.addRelevantTest(trs[i]);
1762              */
1763             }
1764         }
1765 
1766         private void dispatchSelection(TestTree source) {
1767             TreePath[] paths = source.getSelectionPaths();
1768 
1769             if (paths == null || paths.length == 0) {
1770                 return;
1771             } else if (paths.length == 1) {
1772                 Object target = paths[0].getLastPathComponent();
1773 
1774                 // single selection
1775                 if (target instanceof TT_TestNode) {
1776                     //EventQueue.invokeLater(new Selector((TestResult)target, path));
1777                     selectTest(((TT_TestNode) target).getTestResult(), paths[0]);
1778                 } else if (target instanceof TT_BasicNode) {
1779                     //EventQueue.invokeLater(new Selector((TreeNode)target, path));
1780                     selectBranch((TT_BasicNode) target, paths[0]);
1781                 } else {
1782                     // unknown target, ignore it I guess
1783                     if (debug) {
1784                         Debug.println("Unknown node click target in TestTreePanel: ");
1785                         Debug.println("   => " + target);
1786                     } else {
1787                     }
1788                 }
1789             } // outer elseif
1790             else {
1791                 // multiselection
1792                 selectNodes(paths);
1793             }
1794         }
1795 
1796         // MouseAdapter for tree popup
1797         public void mousePressed(MouseEvent e) {
1798             maybeShowPopup(e);
1799         }
1800 
1801         public void mouseReleased(MouseEvent e) {
1802             maybeShowPopup(e);
1803         }
1804 
1805         private void updateSelection(MouseEvent e) {
1806             TreePath pathClicked = tree.getPathForLocation(e.getX(), e.getY());
1807             if(pathClicked != null && tree.getSelectionCount() <= 1 && tree.getSelectionPath() != pathClicked) {
1808                 tree.setSelectionPath(pathClicked);
1809             }
1810         }
1811 
1812         private void maybeShowPopup(MouseEvent e) {
1813             if (e.isPopupTrigger() && e.getComponent() == tree) {
1814                 lastX = e.getX();
1815                 lastY = e.getY();
1816                 updateSelection(e);
1817                 // filter out clicks which don't hit the tree
1818                 /*
1819                 TreePath target = tree.getPathForLocation(lastX, lastY);
1820 
1821                 if (target != null) {
1822                 lastPopupPath = target;
1823                  */
1824                 updatePopupMenu();
1825                 popup.show(e.getComponent(), lastX, lastY);
1826 
1827             /*
1828             // select node which was clicked on
1829             if (tree.getSelectionPath() == null)
1830             tree.setSelectionPath(target);
1831             }
1832              */
1833             }
1834         }
1835 
1836         // ActionListener
1837         public void actionPerformed(ActionEvent e) {
1838             TreePath[] paths = tree.getSelectionPaths();
1839 
1840             if (e.getActionCommand().equals("clear")) {
1841                 if (paths != null && paths.length > 0) {
1842                     clearNodes(tree.getSelectionPaths());
1843                 } else {
1844                     // XXX show error dialog
1845                 }
1846             } /*
1847             else if (e.getActionCommand().equals("info")) {
1848             showNodeInfoDialog(tree.getSelectionPaths());
1849             }
1850              */
1851             else if (e.getActionCommand().equals("run")) {
1852                 if (paths != null && paths.length > 0) {
1853                     // special case check, remove all items if root
1854                     // is selected
1855                     for (int i = 0; i < paths.length; i++) {
1856                         if (paths[i].getPathCount() > 1) {
1857                             continue;
1858                         }
1859                         Object target = paths[i].getPathComponent(0);
1860 
1861                         if (target instanceof TT_TreeNode &&
1862                                 ((TT_TreeNode) target).isRoot()) {
1863                             paths = new TreePath[]{new TreePath(target)};
1864                             break;
1865                         }
1866                     }   // for
1867                     runNodes(paths);
1868                 } else {
1869                     // XXX show error dialog
1870                 }
1871             } else if (e.getActionCommand().equals("refresh")) {
1872                 if (paths != null && paths.length > 0) {
1873                     refreshNodes(tree.getSelectionPaths());
1874                 } else {
1875                     // XXX show error dialog
1876                 }
1877             }
1878         }
1879         private int lastX;
1880         private int lastY;
1881     }
1882 
1883     /**
1884      * Utility class for scheduling an update using the GUI thread.
1885      */
1886     class Selector implements Runnable {
1887 
1888         Selector(TestResult tr, TreePath path) {
1889             this.tr = tr;
1890             this.tp = path;
1891         }
1892 
1893         Selector(TT_TreeNode tn, TreePath path) {
1894             this.tn = tn;
1895             this.tp = path;
1896         }
1897 
1898         public void run() {
1899             if (tr != null) {
1900                 selectTest(tr, tp);
1901             } else {
1902                 selectBranch((TT_BasicNode) tn, tp);
1903             }
1904         }
1905         private TreePath tp;
1906         private TestResult tr;
1907         private TT_TreeNode tn;
1908     }
1909 
1910     private class TreePopupAction extends AbstractAction {
1911 
1912         TreePopupAction(I18NResourceBundle bund, String key) {
1913             desc = bund.getString(key + ".desc");
1914             name = bund.getString(key + ".act");
1915         }
1916 
1917         public void actionPerformed(ActionEvent e) {
1918             if (disposed) {
1919                 return;
1920             }
1921             lastPopupPath = tree.getSelectionPath();
1922             Rectangle loc = tree.getPathBounds(lastPopupPath);
1923             if (loc == null) {
1924                 return;
1925             }
1926             updatePopupMenu();
1927             popup.show(tree,
1928                     loc.x + (int) loc.getWidth(),
1929                     loc.y + (int) loc.getHeight());
1930         }
1931 
1932         public Object getValue(String key) {
1933             if (key == null) {
1934                 throw new NullPointerException();
1935             }
1936             if (key.equals(NAME)) {
1937                 return name;
1938             } else if (key.equals(SHORT_DESCRIPTION)) {
1939                 return desc;
1940             } else {
1941                 return null;
1942             }
1943         }
1944         private String name;
1945         private String desc;
1946     }
1947 
1948     private class PanelModel implements TreePanelModel, Harness.Observer, TestResultTable.Observer {
1949 
1950         PanelModel() {
1951             runningTests = new Hashtable<>();
1952             activeNodes = new Hashtable<>();
1953         }
1954 
1955         public void pauseWork() {
1956             if (treeModel != null) {
1957                 treeModel.pauseWork();
1958             }
1959         }
1960 
1961         public void unpauseWork() {
1962             if (treeModel != null) {
1963                 treeModel.unpauseWork();
1964             }
1965         }
1966 
1967         public void refreshTree() {
1968             String[] openPaths = null;
1969             String[] selectedPaths = null;
1970             if (tree != null) {
1971                 TreePath[] p = tree.snapshotOpenPaths();
1972                 openPaths = treeModel.pathsToStrings(p);
1973 
1974                 selectedPaths = treeModel.pathsToStrings(tree.snapshotSelectedPaths());
1975 
1976             }
1977 
1978             if (treeModel != null) {
1979                 treeModel.notifyFullStructure();
1980             }
1981 
1982 
1983             if (openPaths != null && openPaths.length > 0)
1984                 tree.restorePaths(openPaths, true);
1985 
1986             if (tree != null) {
1987                 tree.restoreSelection(selectedPaths);
1988             }
1989         }
1990 
1991         public void nodeSelected(Object node, TreePath path) {
1992         }
1993 
1994         public void testSelected(TestResult node, TreePath path) {
1995         }
1996 
1997         public void nodeUnSelected(Object node, TreePath path) {
1998         }
1999 
2000         public void testUnSelected(TestResult node, TreePath path) {
2001         }
2002 
2003         public void showNode(Object node, TreePath path) {
2004             selectBranch((TT_BasicNode) (node), path);
2005         }
2006 
2007         public void showNode(String url) {
2008             TreePath path = treeModel.resolveUrl(url);
2009             if (path != null && path.getLastPathComponent() instanceof TT_BasicNode) {
2010                 selectBranch((TT_BasicNode) (path.getLastPathComponent()), path);
2011             }
2012         }
2013 
2014         public void showTest(TestResult node, TreePath path) {
2015             selectTest(node, path);
2016         }
2017 
2018         public void showTest(TestResult tr) {
2019             /*
2020             TreeNode[] path = TestResultTable.getObjectPath(tr);
2021             Object[] fp = new Object[path.length + 1];
2022             System.arraycopy(path, 0, fp, 0, path.length);
2023             fp[fp.length-1] = tr;
2024              */
2025             TreePath tp = treeModel.resolveUrl(tr.getTestName());
2026             if (tp != null && tp.getLastPathComponent() instanceof TT_TreeNode) {
2027                 showTest(tr, tp);
2028             }
2029         }
2030 
2031         public void showTest(String url) {
2032             TestResultTable trt = getTestResultTable();
2033 
2034             if (trt == null) {
2035                 return;
2036             } else {
2037                 TestResult tr = trt.lookup(TestResult.getWorkRelativePath(url));
2038                 if (tr != null) {
2039                     showTest(tr);
2040                 }
2041             }
2042         }
2043 
2044         public void hideNode(Object node, TreePath path) {
2045         }
2046 
2047         public void hideTest(TestResult node, TreePath path) {
2048         }
2049 
2050         public TestResultTable getTestResultTable() {
2051             return treeModel.getTestResultTable();
2052         }
2053 
2054         public String getSelectedTest() {
2055             return activeTest;
2056         }
2057 
2058         public boolean isActive(TestResult tr) {
2059             if (runningTests.containsKey(tr.getTestName())) {
2060                 return true;
2061             } else {
2062                 return false;
2063             }
2064         }
2065 
2066         public TestResult getActive(TestResult tr) {
2067             return runningTests.get(tr.getTestName());
2068         }
2069 
2070         public boolean isActive(TT_TreeNode node) {
2071             if (activeNodes.containsKey(node)) {
2072                 return true;
2073             } else {
2074                 return false;
2075             }
2076         }
2077 
2078         // Harness.Observer
2079         public void startingTestRun(Parameters params) {
2080             runMI.setEnabled(false);
2081             purgeMI.setEnabled(false);
2082             refreshMI.setEnabled(false);
2083         }
2084 
2085         public void startingTest(TestResult tr) {
2086             if (treeModel == null) // may occur during shutdown, see dispose()
2087             {
2088                 return;            // BK needed anymore?  see renderer
2089 
2090             //TestResult lookupTr = treeModel.getTestResultTable().lookup(tr.getWorkRelativePath());
2091             //TreePath tp = treeModel.resolveUrl(tr.getWorkRelativePath());
2092             }
2093 
2094             runningTests.put(tr.getTestName(), tr);
2095 
2096             /*
2097             TreeNode[] trtPath = TestResultTable.getObjectPath(tr);
2098             TT_TreeNode[] nodes = treeModel.translatePath(trtPath, false);
2099             if (nodes == null) {
2100                 if (debug) {
2101                     Debug.println("** " + tr.getTestName());
2102                 }
2103                 return;
2104             }*/
2105 
2106             TreePath tp = treeModel.urlToPath(tr.getTestName());
2107             if (tp != null) {
2108                 Object[] nodes = tp.getPath();
2109 
2110                 for (int i = 0; i < nodes.length; i++) {
2111                     Integer hit = activeNodes.get(nodes[i]);
2112                     if (hit == null) // not currently an active node
2113                     {
2114                         activeNodes.put(nodes[i], ONE);
2115                     } else {
2116                         activeNodes.put(nodes[i],
2117                                 new Integer(1 + hit));
2118                     }
2119                 }
2120             }
2121 
2122             tree.repaint();     // we're allowed to call this on any thread
2123         }
2124 
2125         public void finishedTest(TestResult tr) {
2126             if (treeModel == null) // may occur during shutdown, see dispose()
2127             {
2128                 return;            //runningTests.remove(tr.getTestName());
2129             // it's very important for items to be correctly removed
2130             // from the hashtables
2131             }
2132 
2133             runningTests.remove(tr.getTestName());
2134 
2135             TreeNode[] trtPath = TestResultTable.getObjectPath(tr);
2136             TT_TreeNode[] nodes = treeModel.translatePath(trtPath, false);
2137             if (tr.getTestName().equals(activeTest)) {
2138                 Object[] p = new Object[nodes.length + 1];
2139                 System.arraycopy(nodes, 0, p, 0, nodes.length);
2140                 p[p.length - 1] = tr;
2141                 EventQueue.invokeLater(new Selector(tr, new TreePath(p)));
2142             }
2143 
2144             if (nodes != null) {
2145                 for (int i = 0; i < nodes.length; i++) {
2146                     Integer hit = activeNodes.get(nodes[i]);
2147                     if (hit == null) {
2148                         // should only really happen when test run finished, not
2149                         // during the run
2150                         continue;
2151                     }
2152                     if (hit == ONE) {
2153                         activeNodes.remove(nodes[i]);
2154                     } else {
2155                         int currHits = hit.intValue();
2156                         activeNodes.put(nodes[i],
2157                                 (currHits == 2 ? ONE : new Integer(--currHits)));
2158                     }
2159                 }   // for
2160             }
2161             tree.repaint();     // we're allowed to call this on any thread
2162         }
2163 
2164         public void stoppingTestRun() {
2165         }
2166 
2167         public void finishedTesting() {
2168         }
2169 
2170         public void finishedTestRun(boolean allOK) {
2171             runningTests.clear();
2172             activeNodes.clear();
2173 
2174             runMI.setEnabled(!execModel.isConfiguring());
2175             purgeMI.setEnabled(true);
2176             refreshMI.setEnabled(true);
2177 
2178             tree.repaint();     // we're allowed to call this on any thread
2179         }
2180 
2181         public void error(String msg) {
2182         }
2183 
2184         // TestResultTable.Observer
2185         public void update(TestResult oldValue, TestResult newValue) {
2186             /* BK reenable
2187             if (treeModel == null || treeModel.getTestResultTable() == null)
2188             return;
2189 
2190             // this is primarily to ensure that the subpanel gets updated when
2191             // asynchronous (non-user GUI) events occur
2192             // example - if a "clear" operation is done, which includes the shown test
2193             // SWITCH ONTO EVENT THREAD
2194             if (newValue.getTestName().equals(activeTest)) {
2195             TestResult lookupTr = treeModel.getTestResultTable().lookup(
2196             newValue.getWorkRelativePath());
2197             TreeNode[] nodes = TestResultTable.getObjectPath(lookupTr);
2198 
2199             Object[] p = new Object[nodes.length + 1];
2200             System.arraycopy(nodes, 0, p, 0, nodes.length);
2201             p[p.length-1] = newValue;
2202             EventQueue.invokeLater(new Selector(newValue, new TreePath(p)));
2203             }
2204              */
2205         }
2206 
2207         public void updated(TestResult whichTR) {
2208             // ignore
2209         }
2210         private Hashtable<String,TestResult> runningTests;
2211         private Hashtable<Object, Integer> activeNodes;
2212         private Integer ONE = Integer.valueOf(1);
2213     }
2214 }