1 /*
   2  * $Id$
   3  *
   4  * Copyright (c) 2002, 2011, 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.GridBagLayout;
  31 import java.awt.GridBagConstraints;
  32 import java.awt.event.ActionEvent;
  33 import java.awt.event.ActionListener;
  34 import java.awt.event.ComponentAdapter;
  35 import java.awt.event.ComponentEvent;
  36 import java.io.File;
  37 import java.util.Arrays;
  38 import java.util.Map;
  39 
  40 import javax.swing.Box;
  41 import javax.swing.ButtonGroup;
  42 import javax.swing.BorderFactory;
  43 import javax.swing.DefaultListModel;
  44 import javax.swing.JCheckBox;
  45 import javax.swing.JComboBox;
  46 import javax.swing.JComponent;
  47 import javax.swing.JPanel;
  48 import javax.swing.JLabel;
  49 import javax.swing.JList;
  50 import javax.swing.JRadioButton;
  51 import javax.swing.JScrollPane;
  52 import javax.swing.JTabbedPane;
  53 import javax.swing.JTextField;
  54 import javax.swing.SwingConstants;
  55 import javax.swing.event.ChangeEvent;
  56 import javax.swing.event.ChangeListener;
  57 
  58 import com.sun.interview.Interview;
  59 import com.sun.interview.Question;
  60 import com.sun.javatest.InterviewParameters;
  61 import com.sun.javatest.InitialUrlFilter;
  62 import com.sun.javatest.Keywords;
  63 import com.sun.javatest.KeywordsFilter;
  64 import com.sun.javatest.Parameters.ExcludeListParameters;
  65 import com.sun.javatest.Parameters.MutableExcludeListParameters;
  66 import com.sun.javatest.Status;
  67 import com.sun.javatest.StatusFilter;
  68 import com.sun.javatest.TestFilter;
  69 import com.sun.javatest.TestDescription;
  70 import com.sun.javatest.TestResultTable;
  71 import com.sun.javatest.TestSuite;
  72 import com.sun.javatest.ObservableTestFilter;
  73 import com.sun.javatest.ObservableTestFilter.Observer;
  74 import com.sun.javatest.util.DynamicArray;
  75 import com.sun.javatest.util.StringArray;
  76 import com.sun.javatest.tool.TestTreeSelectionPane;
  77 import com.sun.javatest.tool.UIFactory;
  78 import java.util.HashSet;
  79 
  80 /**
  81  * This filter allows the user to configure the filter using
  82  * the filtering attributes normally found in the parameter
  83  * section of an interview:
  84  * <ul>
  85  * <li>Initial URLs
  86  * <li>Status
  87  * <li>Exclude List
  88  * <li>Keywords
  89  * </ul>
  90  *
  91  *<p>
  92  * The settings for this panel are global, so any changes made affect all
  93  * exec tool instances.
  94  */
  95 class BasicCustomTestFilter extends ConfigurableTestFilter {
  96     // UIFactory parameter assume this class stays in the exec package
  97 
  98     BasicCustomTestFilter(String name, ExecModel e, UIFactory uif) {
  99         super(name, e);
 100         this.uif = uif;
 101         init(null);
 102     }
 103 
 104     BasicCustomTestFilter(Map<String, String> map, ExecModel e, UIFactory uif) {
 105         super(map, e);
 106         this.uif = uif;
 107         init(map);
 108     }
 109 
 110     BasicCustomTestFilter(ExecModel e, UIFactory uif) {
 111         super(uif.getI18NString("basicTf.namePrefix"), e);
 112         this.uif = uif;
 113 
 114         init(null);
 115     }
 116 
 117     ConfigurableTestFilter cloneInstance() {
 118         return new BasicCustomTestFilter(uif.getI18NString("basicTf.namePrefix") +
 119                 instanceCount, execModel, uif);
 120     }
 121 
 122     // override superclass methods
 123     // observers must be static data since all exec tool instances
 124     // must be notified of changes
 125     @Override
 126     public void addObserver(Observer o) {
 127         obs = DynamicArray.append(obs, o);
 128     }
 129 
 130     @Override
 131     public void removeObserver(Observer o) {
 132         obs = DynamicArray.remove(obs, o);
 133     }
 134 
 135     @Override
 136     protected void notifyUpdated(ObservableTestFilter filter) {
 137         // obs will be null if called indirectly via load(map) from the
 138         // superclass constructor, because super(...) is defined to be
 139         // called before instance variables are initialized.
 140         // (Hence the danger of overriding methods called from a superclass
 141         // constructor.)  11/12/02
 142         if (obs == null) {
 143             return;
 144         }
 145 
 146         for (int i = 0; i < obs.length; i++) {
 147             obs[i].filterUpdated(filter);
 148         }
 149     }
 150 
 151     @Override
 152     boolean load(Map<String, String> map) {
 153         boolean result = super.load(map);
 154         activeSettings = new SettingsSnapshot(map);
 155         putSettings(activeSettings);
 156         activateSettings(activeSettings);
 157 
 158         notifyUpdated(this);
 159 
 160         return result;
 161     }
 162 
 163     @Override
 164     boolean save(Map<String, String> map) {
 165         boolean result = super.save(map);
 166         activeSettings.save(map);
 167 
 168         return result;
 169     }
 170 
 171     void update(InterviewParameters ip) {
 172         activateSettings(activeSettings);
 173     }
 174 
 175     synchronized JComponent getEditorPane() {
 176         if (editorPane == null) {
 177             editorPane = uif.createTabbedPane("basicTf.tabs", createTabPanels());
 178             editorPane.setTabPlacement(SwingConstants.TOP);
 179 
 180             try {
 181                 if (activeSettings == null) {
 182                     activeSettings = grabSettings();
 183                 } else {
 184                     putSettings(activeSettings);
 185                 }
 186             } catch (IllegalStateException e) {
 187                 throw new IllegalStateException("Illegal state of BCTF GUI on startup.");
 188             }   // catch
 189 
 190             editorPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
 191         }
 192 
 193         return editorPane;
 194     }
 195 
 196     String commitEditorSettings() {
 197         SettingsSnapshot nowSettings = null;
 198 
 199         try {
 200             nowSettings = grabSettings();
 201         } catch (IllegalStateException e) {
 202             // indicates that the GUI settings are not valid
 203             return e.getMessage();
 204         }   // catch
 205 
 206         if (!activeSettings.equals(nowSettings)) {
 207             return activateSettings(nowSettings);
 208         } else {
 209             return null;
 210         }
 211     }
 212 
 213     void resetEditorSettings() {
 214         // set GUI to match live settings
 215         putSettings(activeSettings);
 216     }
 217 
 218     boolean isEditorChanged() {
 219         SettingsSnapshot nowSettings = null;
 220 
 221         try {
 222             nowSettings = grabSettings();
 223         } catch (IllegalStateException e) {
 224             // indicates that the GUI settings are not valid
 225             return true;
 226         }   // catch
 227         if (activeSettings.equals(nowSettings)) {
 228             return false;
 229         } else {
 230             return true;
 231         }
 232     }
 233 
 234     // TestFilter interface
 235     public boolean accepts(TestDescription td)
 236             throws TestFilter.Fault {
 237         return accepts(td, null);
 238     }
 239 
 240     public boolean accepts(TestDescription td, TestFilter.Observer o)
 241             throws TestFilter.Fault {
 242         if (statusFilterNeedsUpdate)
 243             updateStatusFilter();
 244 
 245         for (int i = 0; i < activeFilters.length; i++) {
 246             if (activeFilters[i] != null &&
 247                     !activeFilters[i].accepts(td)) {
 248                 if (o != null) {
 249                     o.rejected(td, activeFilters[i]);
 250                 } else {
 251                 }
 252 
 253                 return false;
 254             }   // outer if
 255         }   // for
 256 
 257         return true;
 258     }
 259 
 260     public String getBaseName() {
 261         return NAME;
 262     }
 263 
 264     public String getName() {
 265         return instanceName;
 266     }
 267 
 268     public String getReason() {
 269         return REASON;
 270     }
 271 
 272     public String getDescription() {
 273         return DESCRIPTION;
 274     }
 275 
 276     // ----- PRIVATE -----
 277     private void init(Map<String, String> map) {
 278         if (NAME == null) {
 279             NAME = uif.getI18NString("basicTf.name");
 280         }
 281 
 282         if (REASON == null) {
 283             REASON = uif.getI18NString("basicTf.reason");
 284         }
 285 
 286         if (DESCRIPTION == null) {
 287             DESCRIPTION = uif.getI18NString("basicTf.description");
 288         }
 289 
 290         if (map != null) {
 291             activeSettings = new SettingsSnapshot(map);
 292         } else {
 293             activeSettings = new SettingsSnapshot();
 294         }
 295 
 296         instanceCount++;
 297 
 298         // not using this from superclass
 299         observers = null;
 300         activateSettings(activeSettings);
 301     }
 302 
 303     /**
 304      * Make the given settings the active ones.
 305      * Except under exceptional conditions, the filters are recreated and
 306      * an update message is sent.
 307      */
 308     private String activateSettings(SettingsSnapshot nowSettings) {
 309         KeywordsFilter newKeyFilter = null;
 310         InitialUrlFilter newUrlFilter = null;
 311         TestFilter newJtxFilter = null;
 312         StatusFilter newStatusFilter = null;
 313         TestFilter newTsfFilter = null;
 314 
 315         InterviewParameters ip = execModel.getInterviewParameters();
 316         TestSuite ts = execModel.getTestSuite();
 317         updateInterviewObserver(ip);
 318 
 319         // recreate filters
 320         if (nowSettings.urlsEnabled) {
 321             // converting to strings to avoid any confusion
 322             // files vs urls
 323             if (nowSettings.initialUrls != null) {
 324                 newUrlFilter = new InitialUrlFilter(nowSettings.initialUrls);
 325             }
 326         }
 327 
 328         if (nowSettings.keywordsEnabled) {
 329             try {
 330                 String[] validKeywords = ts.getKeywords();
 331                 HashSet<String> validKeywordsSet;
 332                 if (validKeywords == null) {
 333                     validKeywordsSet = null;
 334                 } else {
 335                     validKeywordsSet = new HashSet<>(Arrays.asList(validKeywords));
 336                 }
 337 
 338                 Keywords kw = Keywords.create(kwModeToType(nowSettings.keyChoice),
 339                         nowSettings.keyString, validKeywordsSet);
 340                 newKeyFilter = new KeywordsFilter(kw);
 341             } catch (Keywords.Fault f) {
 342                 return f.getMessage();
 343             }
 344 
 345         }
 346 
 347         if (nowSettings.statusEnabled) {
 348             // this filter won't work without a TRT
 349             TestResultTable trt = execModel.getActiveTestResultTable();
 350             if (trt != null) {
 351                 newStatusFilter = new StatusFilter(
 352                         nowSettings.statusFields,
 353                         trt);
 354                 statusFilterNeedsUpdate = false;
 355             } else {
 356                 statusFilterNeedsUpdate = true;
 357             }
 358         } else {
 359             // to clear any old status
 360             statusFilterNeedsUpdate = false;
 361         }
 362 
 363         if (nowSettings.jtxEnabled) {
 364             // we only support copying exclude list from interview
 365             // right now
 366             if (ip != null) {
 367                 // may set var. to null, but that's okay
 368                 newJtxFilter = ip.getExcludeListFilter();
 369             }
 370         }
 371 
 372         if (nowSettings.tsfEnabled) {
 373             // we only support copying exclude list from interview
 374             // right now
 375             if (ip != null && ts != null) {
 376                 // may set filter to null, but that's okay
 377                 newTsfFilter = ts.createTestFilter(ip.getEnv());
 378             }
 379         }
 380 
 381         // can abort anytime before this
 382         // commit now
 383         keyFilter = newKeyFilter;
 384         urlFilter = newUrlFilter;
 385         jtxFilter = newJtxFilter;
 386         statusFilter = newStatusFilter;
 387         tsfFilter = newTsfFilter;
 388 
 389         // initialize the array if not yet done so
 390         if (activeFilters == null) {
 391             activeFilters = new TestFilter[NUM_FILTERS];
 392         }
 393 
 394         activeFilters[KEY_FILTER] = keyFilter;
 395         activeFilters[URL_FILTER] = urlFilter;
 396         activeFilters[JTX_FILTER] = jtxFilter;
 397         activeFilters[STATUS_FILTER] = statusFilter;
 398         activeFilters[TSS_FILTER] = tsfFilter;
 399 
 400         activeSettings = nowSettings;
 401         updateExcludeInfo();
 402 
 403         // notify observers
 404         notifyUpdated(this);
 405         return null;
 406     }
 407 
 408     private void updateStatusFilter() {
 409         if (!statusFilterNeedsUpdate)
 410             return;
 411 
 412         TestResultTable trt = execModel.getActiveTestResultTable();
 413         if (trt != null) {
 414             activeFilters[STATUS_FILTER] = new StatusFilter(
 415                     activeSettings.statusFields,
 416                     trt);
 417             statusFilterNeedsUpdate = false;
 418         }
 419     }
 420 
 421     /**
 422      * Attach observer to the given interview.
 423      * Automatically detaches from other interviews being observed.
 424      * @param ip The interview to observe.  For convenience, if this param
 425      *        is null, any current observers will be detached.
 426      */
 427     private void updateInterviewObserver(InterviewParameters ip) {
 428         if (ip == null && intObs != null) {
 429             intObs.getInterview().removeObserver(intObs);
 430         } else if (intObs == null || intObs.getInterview() != ip) {
 431             if (intObs != null) {
 432                 intObs.getInterview().removeObserver(intObs);
 433             }
 434 
 435             if (ip != null) {
 436                 intObs = new InterviewObserver(ip);
 437                 ip.addObserver(intObs);
 438             }
 439         }
 440     }
 441 
 442     private void updateExcludeInfo() {
 443         // check to see if GUI is init-ed
 444         if (jtxMode == null) {
 445             return;
 446         }
 447 
 448         InterviewParameters ip = execModel.getInterviewParameters();
 449 
 450         if (ip == null) // nothing to do
 451         {
 452             return;
 453         }
 454 
 455         boolean isUnknown = true;
 456         ExcludeListParameters elp = ip.getExcludeListParameters();
 457 
 458         if (ip != null) {
 459             elp = ip.getExcludeListParameters();
 460             if (elp instanceof MutableExcludeListParameters) {
 461                 MutableExcludeListParameters melp = (MutableExcludeListParameters) elp;
 462                 int mode = melp.getExcludeMode();
 463                 switch (mode) {
 464                     case MutableExcludeListParameters.NO_EXCLUDE_LIST:
 465                         jtxMode.setText(uif.getI18NString("basicTf.exclude.mode.none"));
 466                         setExcludeFiles(null);
 467                         isUnknown = false;
 468                         break;
 469                     case MutableExcludeListParameters.INITIAL_EXCLUDE_LIST:
 470                         jtxMode.setText(uif.getI18NString("basicTf.exclude.mode.initial"));
 471                         setExcludeFiles(melp.getExcludeFiles());
 472                         isUnknown = false;
 473                         break;
 474                     case MutableExcludeListParameters.LATEST_EXCLUDE_LIST:
 475                         jtxMode.setText(uif.getI18NString("basicTf.exclude.mode.latest"));
 476                         setExcludeFiles(null);
 477                         isUnknown = false;
 478                         break;
 479                     case MutableExcludeListParameters.CUSTOM_EXCLUDE_LIST:
 480                         jtxMode.setText(uif.getI18NString("basicTf.exclude.mode.custom"));
 481                         setExcludeFiles(melp.getCustomExcludeFiles());
 482                         isUnknown = false;
 483                         break;
 484                 }   // switch
 485 
 486             }
 487         }
 488 
 489         // done here to avoid duplicating code above
 490         if (isUnknown) {
 491             jtxMode.setText(uif.getI18NString("basicTf.exclude.mode.unknown"));
 492             jtxFiles.removeAllElements();
 493         }
 494     }
 495 
 496     /**
 497      * Adds exclude list files to the list.
 498      * If null or zero length, the list is cleared.
 499      * @param files The files to add.  Null ok.
 500      */
 501     private void setExcludeFiles(File[] files) {
 502         jtxFiles.removeAllElements();
 503 
 504         if (files == null || files.length == 0) {
 505             return;
 506         } else {
 507             for (int i = 0; i < files.length; i++) {
 508                 jtxFiles.addElement(files[i].getPath());
 509             }
 510         }
 511     }
 512 
 513     private JComponent[] createTabPanels() {
 514         JComponent[] items = {createTestsPanel(), createKeywordPanel(),
 515             createStatusPanel(), createExcludePanel(),
 516             createSpecialPanel()};
 517 
 518         return items;
 519     }
 520 
 521     // KEYWORD PANEL
 522     private JComponent createKeywordPanel() {
 523         JPanel p = uif.createPanel(
 524                 "basicTf.keywords.mainPanel", new GridBagLayout(), false);
 525         p.setName("keywords");
 526 
 527         GridBagConstraints c = new GridBagConstraints();
 528         c.gridwidth = GridBagConstraints.REMAINDER;
 529         c.fill = GridBagConstraints.HORIZONTAL;
 530         c.weightx = 1;
 531 
 532         keyBtnGrp = new ButtonGroup();
 533         keyAllBtn = uif.createRadioButton("basicTf.keywords.all", keyBtnGrp);
 534         keyAllBtn.setMnemonic(uif.getI18NString("basicTf.keywords.all.mne").charAt(0));
 535         p.add(keyAllBtn, c);
 536 
 537         keyMatchBtn = uif.createRadioButton("basicTf.keywords.match", keyBtnGrp);
 538         keyMatchBtn.setMnemonic(uif.getI18NString("basicTf.keywords.match.mne").charAt(0));
 539         keyMatchBtn.addChangeListener(new ChangeListener() {
 540 
 541             public void stateChanged(ChangeEvent e) {
 542                 enableKeywordFields();
 543             }
 544         });
 545         c.weightx = 0;
 546         c.gridwidth = 1;
 547         p.add(keyMatchBtn, c);
 548 
 549         String[] kc = {ANY_OF, ALL_OF, EXPR};
 550         keywordsChoice = uif.createChoice("basicTf.keywords.choice", kc);
 551         p.add(keywordsChoice, c);
 552 
 553         keywordsField = uif.createInputField("basicTf.keywords.field", 20);
 554         keywordsField.setEditable(true);
 555         uif.setAccessibleInfo(keywordsField, "basicTf.keywords.field");
 556         c.fill = GridBagConstraints.HORIZONTAL;
 557         c.gridwidth = GridBagConstraints.REMAINDER;
 558         c.weightx = 1;
 559         p.add(keywordsField, c);
 560 
 561         keyAllBtn.setSelected(true);
 562         enableKeywordFields();
 563         return p;
 564     }
 565 
 566     private void enableKeywordFields() {
 567         boolean b = keyMatchBtn.isSelected();
 568         keywordsChoice.setEnabled(b);
 569         keywordsField.setEnabled(b);
 570     }
 571 
 572     // STATUS PANEL
 573     private JComponent createStatusPanel() {
 574         JPanel p = uif.createPanel(
 575                 "basicTf.status.mainPanel", new GridBagLayout(), false);
 576         p.setName("status");
 577 
 578         GridBagConstraints c = new GridBagConstraints();
 579         c.anchor = GridBagConstraints.WEST;
 580         c.gridwidth = GridBagConstraints.REMAINDER;
 581 
 582         statusBtnGrp = new ButtonGroup();
 583         statusAllBtn = uif.createRadioButton("basicTf.status.all", statusBtnGrp);
 584         statusAllBtn.setMnemonic(uif.getI18NString("basicTf.status.all.mne").charAt(0));
 585         p.add(statusAllBtn, c);
 586 
 587         statusAnyOfBtn = uif.createRadioButton("basicTf.status.anyOf", statusBtnGrp);
 588         statusAnyOfBtn.setMnemonic(uif.getI18NString("basicTf.status.anyOf.mne").charAt(0));
 589         statusAnyOfBtn.addChangeListener(new ChangeListener() {
 590 
 591             public void stateChanged(ChangeEvent e) {
 592                 enableStatusFields();
 593             }
 594         });
 595         c.gridwidth = 1;
 596         c.weightx = 0;
 597         p.add(statusAnyOfBtn, c);
 598 
 599         JPanel row = new JPanel(new GridBagLayout());
 600         row.setBorder(BorderFactory.createEtchedBorder());
 601         GridBagConstraints rc = new GridBagConstraints();
 602         rc.insets.left = 10;
 603 
 604         statusChecks[Status.PASSED] =
 605                 uif.createCheckBox("basicTf.status.prev.passed", false);
 606         row.add(statusChecks[Status.PASSED], rc);
 607 
 608         statusChecks[Status.FAILED] =
 609                 uif.createCheckBox("basicTf.status.prev.failed", true);
 610         row.add(statusChecks[Status.FAILED], rc);
 611 
 612         statusChecks[Status.ERROR] =
 613                 uif.createCheckBox("basicTf.status.prev.error", true);
 614         row.add(statusChecks[Status.ERROR], rc);
 615 
 616         rc.insets.right = 10;
 617         statusChecks[Status.NOT_RUN] =
 618                 uif.createCheckBox("basicTf.status.prev.notRun", true);
 619         row.add(statusChecks[Status.NOT_RUN], rc);
 620         uif.setToolTip(row, "basicTf.status.prev");
 621 
 622         statusAllBtn.setSelected(true);
 623         enableStatusFields();
 624         p.add(row, c);
 625 
 626         return p;
 627     }
 628 
 629     private void enableStatusFields() {
 630         boolean enable = statusAnyOfBtn.isEnabled() && statusAnyOfBtn.isSelected();
 631         for (int i = 0; i < statusChecks.length; i++) {
 632             statusChecks[i].setEnabled(enable);
 633         }
 634     }
 635 
 636     // INIT URLS
 637     private JComponent createTestsPanel() {
 638         final JPanel p = uif.createPanel(
 639                 "basicTf.tests.mainPanel", new BorderLayout(), false);
 640         p.setName("tests");
 641 
 642         // more configure ...
 643         lastTrt = execModel.getActiveTestResultTable();
 644         testsField = new TestTreeSelectionPane(lastTrt);
 645 
 646         p.add(testsField, BorderLayout.CENTER);
 647 
 648         p.addComponentListener(new ComponentAdapter() {
 649 
 650             public void componentShown(ComponentEvent e) {
 651                 TestResultTable nowTrt = execModel.getActiveTestResultTable();
 652                 // replaces widget with an updated one
 653                 // only really important if the test suite contents (structure)
 654                 // changes
 655                 // this code basically keeps it in sync with the main tree,
 656                 // otherwise it might be left behind watching a temporary TRT
 657                 if (nowTrt != lastTrt) {
 658                     TestTreeSelectionPane newTree = new TestTreeSelectionPane(nowTrt);
 659                     String[] paths = testsField.getSelection();     // save
 660                     p.remove(testsField);
 661                     testsField = newTree;
 662                     testsField.setSelection(paths);                 // restore
 663                     p.add(testsField, BorderLayout.CENTER);
 664                     lastTrt = nowTrt;
 665                 }
 666             }
 667         });
 668 
 669         return p;
 670     }
 671 
 672     // JTX lists
 673     private JComponent createExcludePanel() {
 674         JPanel p = new JPanel(new GridBagLayout());
 675         p.setName("exclude");
 676 
 677         GridBagConstraints c = new GridBagConstraints();
 678         c.anchor = GridBagConstraints.NORTHWEST;
 679         c.ipadx = 10;
 680         c.gridwidth = 1;
 681         c.gridx = 0;
 682         c.gridy = 0;
 683         c.fill = GridBagConstraints.NONE;
 684 
 685         // column 1
 686         c.gridwidth = 3;
 687         jtxCheckBox = uif.createCheckBox("basicTf.exclude", false);
 688         jtxCheckBox.setMnemonic(uif.getI18NString("basicTf.exclude.mne").charAt(0));
 689         p.add(jtxCheckBox, c);
 690 
 691         // create some margins
 692         c.gridy = 1;
 693         p.add(Box.createVerticalStrut(5), c);
 694 
 695         c.gridy = 2;
 696         c.gridwidth = 1;
 697 
 698         c.gridheight = 2;
 699         p.add(Box.createHorizontalStrut(8), c);
 700 
 701         c.gridheight = 1;
 702         c.gridx = 2;
 703 
 704         // labels
 705         final JLabel modeLab = uif.createLabel("basicTf.exclude.mode");
 706         modeLab.setDisplayedMnemonic(uif.getI18NString("basicTf.exclude.mode.mne").charAt(0));
 707         modeLab.setEnabled(jtxCheckBox.isSelected());
 708         p.add(modeLab, c);
 709 
 710         c.gridy = 3;
 711         final JLabel fileLab = uif.createLabel("basicTf.exclude.file");
 712         fileLab.setDisplayedMnemonic(uif.getI18NString("basicTf.exclude.file.mne").charAt(0));
 713         fileLab.setEnabled(jtxCheckBox.isSelected());
 714         p.add(fileLab, c);
 715 
 716         // column 2
 717         c.gridy = 2;
 718         c.gridx = 3;
 719         c.fill = GridBagConstraints.HORIZONTAL;
 720         jtxMode = uif.createOutputField("basicTf.exclude.mode", modeLab);
 721         jtxMode.setBorder(BorderFactory.createEmptyBorder());
 722         jtxMode.setEditable(false);
 723         jtxMode.setEnabled(jtxCheckBox.isSelected());
 724         uif.setAccessibleInfo(jtxMode, "basicTf.exclude.mode");
 725         p.add(jtxMode, c);
 726 
 727         c.gridy = 3;
 728         c.weightx = 2;
 729         c.weighty = 2;
 730         c.fill = GridBagConstraints.BOTH;
 731         jtxFiles = new DefaultListModel<String>();
 732         jtxFileList = uif.createList("basicTf.exclude.file", jtxFiles);
 733         jtxFileList.setEnabled(jtxCheckBox.isSelected());
 734         uif.setAccessibleInfo(jtxFileList, "basicTf.exclude.file");
 735         fileLab.setLabelFor(jtxFileList);
 736 
 737         // might need to add a scroll panel here
 738         p.add(uif.createScrollPane(jtxFileList, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
 739                 JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED), c);
 740 
 741         jtxCheckBox.addActionListener(new ActionListener() {
 742 
 743             public void actionPerformed(ActionEvent e) {
 744                 jtxFileList.setEnabled(jtxCheckBox.isSelected());
 745                 jtxMode.setEnabled(jtxCheckBox.isSelected());
 746                 modeLab.setEnabled(jtxCheckBox.isSelected());
 747                 fileLab.setEnabled(jtxCheckBox.isSelected());
 748             }
 749         });
 750 
 751         return p;
 752     }
 753 
 754     // test suite specific filter
 755     private JComponent createSpecialPanel() {
 756         String k = "basicTf.tsf";
 757         JPanel p = uif.createPanel("basicTf.tsf", new BorderLayout(), false);
 758         p.setName("special");
 759 
 760         tsfCheckBox = uif.createCheckBox("basicTf.tsf", false);
 761         tsfCheckBox.setMnemonic(uif.getI18NString("basicTf.tsf.mne").charAt(0));
 762         p.add(tsfCheckBox, BorderLayout.CENTER);
 763 
 764         return p;
 765     }
 766 
 767     /**
 768      * Capture the settings in the current GUI.
 769      * @return A snapshot of the settings currently entred into the GUI.
 770      * @throws IllegalStateException If any part of the GUI has a state which
 771      *         does not produce a consistent setting.  The message of this
 772      *         exception will contain an internationalized message to help the
 773      *         user resolve the problem.
 774      */
 775     private SettingsSnapshot grabSettings() {
 776         SettingsSnapshot shot = new SettingsSnapshot();
 777 
 778         // grad top level togggle state of each type
 779         shot.keywordsEnabled = !keyAllBtn.isSelected();
 780         //shot.urlsEnabled = !allTestsBtn.isSelected();
 781         shot.urlsEnabled = true;        // always enabled in current impl.
 782         shot.statusEnabled = statusAnyOfBtn.isSelected();
 783         shot.jtxEnabled = jtxCheckBox.isSelected();
 784         shot.tsfEnabled = tsfCheckBox.isSelected();
 785 
 786         // grab status checkbox state
 787         boolean oneSelected = false;
 788         shot.statusFields = new boolean[statusChecks.length];
 789         for (int i = 0; i < statusChecks.length; i++) {
 790             shot.statusFields[i] = statusChecks[i].isSelected();
 791             oneSelected = oneSelected || shot.statusFields[i];
 792         }
 793 
 794         if (shot.statusEnabled && !oneSelected) {
 795             throw new IllegalStateException(uif.getI18NString("basicTf.badStatus"));
 796         }
 797 
 798         // init. urls
 799         shot.initialUrls = testsField.getSelection();
 800 
 801         // special case post processing for root selection
 802         // if length is one and that string is zero length, that indicates
 803         // a root selection
 804         if (shot.initialUrls != null &&
 805                 shot.initialUrls.length == 1 &&
 806                 shot.initialUrls[0].length() == 0) {
 807             shot.initialUrls = null;
 808         }
 809 
 810         // keywords
 811         shot.keyChoice = (String) (keywordsChoice.getSelectedItem());
 812         shot.keyString = keywordsField.getText();
 813 
 814         return shot;
 815     }
 816 
 817     /**
 818      * Put a set of settings into the GUI.
 819      */
 820     private void putSettings(SettingsSnapshot s) {
 821         // no GUI, don't do anything
 822         if (editorPane == null) {
 823             return;
 824         }
 825 
 826         keyAllBtn.setSelected(!s.keywordsEnabled);
 827         keyMatchBtn.setSelected(s.keywordsEnabled);
 828 
 829         statusAllBtn.setSelected(!s.statusEnabled);
 830         statusAnyOfBtn.setSelected(s.statusEnabled);
 831 
 832         jtxCheckBox.setSelected(s.jtxEnabled);
 833         tsfCheckBox.setSelected(s.tsfEnabled);
 834 
 835         for (int i = 0; i < statusChecks.length; i++) {
 836             statusChecks[i].setSelected(s.statusFields[i]);
 837         }
 838 
 839         testsField.setSelection(s.initialUrls);
 840 
 841         keywordsChoice.setSelectedItem(s.keyChoice);
 842         keywordsField.setText(s.keyString);
 843 
 844         updateExcludeInfo();
 845     }
 846 
 847     // Utility methods
 848     private static String kwModeToType(String mode) {
 849         if (mode == ALL_OF) {
 850             return "all of";
 851         } else if (mode == ANY_OF) {
 852             return "any of";
 853         } else {
 854             return EXPR;
 855         }
 856     }
 857 
 858     private Observer[] obs = new Observer[0];
 859     private static int instanceCount;
 860     private TestResultTable lastTrt;
 861     private InterviewObserver intObs;
 862 
 863     private static int NUM_FILTERS = 5;
 864     private static int KEY_FILTER = 0;
 865     private static int URL_FILTER = 1;
 866     private static int JTX_FILTER = 2;
 867     private static int STATUS_FILTER = 3;
 868     private static int TSS_FILTER = 4;
 869 
 870     private SettingsSnapshot activeSettings;
 871     private TestFilter[] activeFilters;     // for convenience
 872     private KeywordsFilter keyFilter;
 873     private InitialUrlFilter urlFilter;
 874     private TestFilter jtxFilter;
 875     private StatusFilter statusFilter;
 876     private TestFilter tsfFilter;           // test suite filter
 877     private final UIFactory uif;
 878     private JTabbedPane editorPane;
 879 
 880     private boolean statusFilterNeedsUpdate;
 881 
 882     // keyword info
 883     private ButtonGroup keyBtnGrp;
 884     private JRadioButton keyAllBtn;
 885     private JRadioButton keyMatchBtn;
 886     private JComboBox<String> keywordsChoice;
 887     private JTextField keywordsField;
 888     private static final String ALL_OF = "allOf";
 889     private static final String ANY_OF = "anyOf";
 890     private static final String EXPR = "expr";
 891 
 892     // status info
 893     private ButtonGroup statusBtnGrp;
 894     private JRadioButton statusAllBtn;
 895     private JRadioButton statusAnyOfBtn;
 896     private JCheckBox[] statusChecks = new JCheckBox[Status.NUM_STATES];
 897 
 898     // tests info
 899     private ButtonGroup testsBtnGrp;
 900     //private JRadioButton allTestsBtn;
 901     //private JRadioButton selectTestsBtn;
 902     private TestTreeSelectionPane testsField;
 903 
 904     // checkboxes to enable exclude list from interview and test suite filter
 905     private JCheckBox jtxCheckBox;
 906     private JCheckBox tsfCheckBox;
 907 
 908     // jtx info fields
 909     private JTextField jtxMode;
 910     private JList<?> jtxFileList;
 911     private DefaultListModel<String> jtxFiles;
 912     private static String NAME,  REASON,  DESCRIPTION;
 913 
 914     /**
 915      * Necessary to track changes which occur in the active interview.
 916      */
 917     private class InterviewObserver implements Interview.Observer {
 918 
 919         InterviewObserver(InterviewParameters i) {
 920             interview = i;
 921         }
 922 
 923         InterviewParameters getInterview() {
 924             return interview;
 925         }
 926 
 927         public void currentQuestionChanged(Question q) {
 928             // ignore
 929         }
 930 
 931         public void pathUpdated() {
 932             TestFilter underTest;
 933             boolean needsUpdate = false;
 934             updateExcludeInfo();
 935 
 936             // determine if jtx and tsf filters were updated
 937             if (activeSettings.jtxEnabled) {
 938                 // we only support copying exclude list from interview
 939                 // right now
 940                 InterviewParameters ip = execModel.getInterviewParameters();
 941 
 942                 if (ip != null) {
 943                     // may set var. to null, but that's okay
 944                     underTest = ip.getExcludeListFilter();
 945                     if (underTest != null &&
 946                             (jtxFilter == null || !underTest.equals(jtxFilter))) {
 947                         needsUpdate = true;
 948                     } else if (jtxFilter != null) // jtx filter now null
 949                     {
 950                         needsUpdate = true;
 951                     }
 952                 }
 953             }
 954 
 955             if (needsUpdate) {
 956                 activateSettings(activeSettings);
 957                 return;     // rest of checking no longer needed
 958             }
 959 
 960             if (activeSettings.tsfEnabled) {
 961                 // we only support copying exclude list from interview
 962                 // right now
 963                 InterviewParameters ip = execModel.getInterviewParameters();
 964                 TestSuite ts = execModel.getTestSuite();
 965 
 966                 if (ip != null && ts != null) {
 967                     underTest = ts.createTestFilter(ip.getEnv());
 968                     if (underTest != null) {
 969                         if (tsfFilter == null || !underTest.equals(tsfFilter)) {
 970                             needsUpdate = true;
 971                         } else {
 972                         }
 973                     } else if (tsfFilter != null) // tsf filter now null
 974                     {
 975                         needsUpdate = true;
 976                     } else {
 977                     }
 978                 } else {
 979                 }
 980             }
 981 
 982             if (needsUpdate) {
 983                 activateSettings(activeSettings);
 984                 return;     // rest of checking no longer needed
 985             }
 986         }
 987         private InterviewParameters interview;
 988     }
 989 
 990     private class SettingsSnapshot {
 991 
 992         SettingsSnapshot() {
 993             statusFields = new boolean[Status.NUM_STATES];
 994             urlsEnabled = true;
 995             keyChoice = EXPR;
 996             keyString = "";
 997         }
 998 
 999         SettingsSnapshot(Map<String, String> m) {
1000             this();
1001             load(m);
1002         }
1003 
1004         public boolean equals(Object settings) {
1005 
1006             if (settings == null) {
1007                 return false;
1008             }
1009 
1010             SettingsSnapshot incoming = (SettingsSnapshot) settings;
1011 
1012             if (!Arrays.equals(initialUrls, incoming.initialUrls)) {
1013                 return false;
1014             }
1015 
1016             if (keywordsEnabled != incoming.keywordsEnabled) {
1017                 return false;
1018             } else {
1019                 if (keyChoice != incoming.keyChoice) {
1020                     return false;
1021                 }
1022 
1023                 // destination of structure is to handle null values
1024                 if (keyString == null) {
1025                     if (keyString != incoming.keyString) {
1026                         return false;
1027                     } else {
1028                     }
1029                 } else {
1030                     if (!keyString.equals(incoming.keyString)) {
1031                         return false;
1032                     } else {
1033                     }
1034                 }
1035             }   // outer else
1036 
1037             if (statusEnabled != incoming.statusEnabled) {
1038                 return false;
1039             } else if (!Arrays.equals(statusFields, incoming.statusFields)) {
1040                 return false;
1041             }
1042 
1043             if (jtxEnabled != incoming.jtxEnabled) {
1044                 return false;
1045             }
1046 
1047             if (tsfEnabled != incoming.tsfEnabled) {
1048                 return false;
1049             }
1050 
1051             return true;
1052         }   // equals()
1053 
1054         void save(Map<String, String> map) {
1055             map.put(MAP_URL_ENABLE, booleanToInt(urlsEnabled));
1056             map.put(MAP_KEY_ENABLE, booleanToInt(keywordsEnabled));
1057             map.put(MAP_STATUS_ENABLE, booleanToInt(statusEnabled));
1058             map.put(MAP_JTX_ENABLE, booleanToInt(jtxEnabled));
1059             map.put(MAP_TSF_ENABLE, booleanToInt(tsfEnabled));
1060 
1061             for (int i = 0; i < statusFields.length; i++) {
1062                 map.put(MAP_STATUS_PREFIX + i, booleanToInt(statusFields[i]));
1063             }
1064 
1065             map.put(MAP_URLS, StringArray.join(initialUrls));
1066 
1067             map.put(MAP_KEY_CHOICE, keyChoice);
1068             map.put(MAP_KEY_STRING, keyString);
1069         }
1070 
1071         void load(Map<String, String> map) {
1072             urlsEnabled = intToBoolean((map.get(MAP_URL_ENABLE)));
1073             keywordsEnabled = intToBoolean((map.get(MAP_KEY_ENABLE)));
1074             statusEnabled = intToBoolean((map.get(MAP_STATUS_ENABLE)));
1075             jtxEnabled = intToBoolean((map.get(MAP_JTX_ENABLE)));
1076             tsfEnabled = intToBoolean((map.get(MAP_TSF_ENABLE)));
1077 
1078             for (int i = 0; i < Status.NUM_STATES; i++) {
1079                 statusFields[i] = intToBoolean((map.get(MAP_STATUS_PREFIX + i)));
1080             }   // for
1081 
1082             initialUrls = StringArray.split((map.get(MAP_URLS)));
1083 
1084             keyChoice = map.get(MAP_KEY_CHOICE);
1085             keyString = map.get(MAP_KEY_STRING);
1086 
1087             validate();
1088         }
1089 
1090         private void validate() {
1091             // conserve strings, but also sync with the actual
1092             // objects in the choice list for easy use
1093             if (keyChoice.equals(ALL_OF)) {
1094                 keyChoice = ALL_OF;
1095             } else if (keyChoice.equals(ANY_OF)) {
1096                 keyChoice = ANY_OF;
1097             } else if (keyChoice.equals(EXPR)) {
1098                 keyChoice = EXPR;
1099             } else {
1100                 keyChoice = ALL_OF;     // unknown, use default
1101             }
1102         }
1103 
1104         String booleanToInt(boolean val) {
1105             if (val) {
1106                 return "1";
1107             } else {
1108                 return "0";
1109             }
1110         }
1111 
1112         boolean intToBoolean(String num) {
1113             if (num == null) {
1114                 return false;
1115             }
1116 
1117             if (num.equals("1")) {
1118                 return true;
1119             } else {
1120                 return false;
1121             }
1122         }
1123         boolean urlsEnabled;
1124         boolean keywordsEnabled;
1125         boolean statusEnabled;
1126         boolean jtxEnabled;
1127         boolean tsfEnabled;
1128         boolean[] statusFields;
1129         String[] initialUrls;
1130         String keyChoice;
1131         String keyString;
1132         private static final String MAP_URL_ENABLE = "urlsEnabled";
1133         private static final String MAP_KEY_ENABLE = "keyEnabled";
1134         private static final String MAP_STATUS_ENABLE = "statusEnable";
1135         private static final String MAP_JTX_ENABLE = "jtxEnable";
1136         private static final String MAP_TSF_ENABLE = "tsfEnable";
1137         private static final String MAP_URLS = "urls";
1138         private static final String MAP_KEY_CHOICE = "keyChoice";
1139         private static final String MAP_KEY_STRING = "keyString";
1140         private static final String MAP_STATUS_PREFIX = "status";
1141     }
1142 }